2

I have an input image A and a resulting image B with the size 800x600 stored in YUV420 format and I need to scale image A into 100x100 size and place it into resulting image B at some point (x=100, y=100). To decrease memory and CPU usage I put swscale result right into final B image.

Here is a code snippets (pretty straightforward):

//here we a creating sws context for scaling into 100x100
sws_ctx = sws_getCachedContext(sws_ctx, frame.hdr.width, frame.hdr.height, AV_PIX_FMT_YUV420P,
                               100, 100, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr);

Next we create corresponding slice and strides describing image A

    int src_y_plane_sz = frame.hdr.width * frame.hdr.height;
    int src_uv_plane_sz = src_y_plane_sz / 2;
    std::int32_t src_stride[] = {
        frame.hdr.width,
        frame.hdr.width / 2,
        frame.hdr.width / 2,
        0};

    const uint8_t* const src_slice[] = {
        &frame.raw_frame[0],
        &frame.raw_frame[0] + src_y_plane_sz,
        &frame.raw_frame[0] + src_y_plane_sz + src_uv_plane_sz,
        nullptr};

Now doing the same for a destination B image

    std::int32_t dst_stride[] = {
        current_frame.hdr.width,
        current_frame.hdr.width /2,
        current_frame.hdr.width /2,
        0
    };

    std::int32_t y_plane_sz = current_frame.hdr.width * current_frame.hdr.height;
    std::int32_t uv_plane_sz = y_plane_sz / 2;

    //calculate offset in slices for x=100, y=100 position
    std::int32_t y_offset = current_frame.hdr.width * 100 + 100;

    uint8_t* const dst_slice[] = {
        &current_frame.raw_frame[0] + y_offset,
        &current_frame.raw_frame[0] + y_plane_sz + y_offset / 2,
        &current_frame.raw_frame[0] + y_plane_sz + uv_plane_sz + y_offset / 2,
        nullptr};

After all - calling swscale

    int ret = sws_scale(sws_ctx, src_slice, src_stride, 0, frame.hdr.height,
                        dst_slice, dst_stride);

After using a testing sequence I having some invalid result with the following problems:

  1. Y component got some padding line
  2. UV components got misplaced - they are a bit lower then original Y components.

Artefacts

Does anyone have had the same problems with swscale function? I am pretty new to this FFmpeg library collection so I am open to any opinions how to perform this task correctly.

FFmpeg version used 3.3

armigero
  • 73
  • 6

2 Answers2

1

YUV420 format scales both width and height of the image by two. That is each chromatic plane is 4 times smaller than luma plane:

int src_uv_plane_sz = src_y_plane_sz / 4;

Also i'm not sure whether calculated stride values are correct. Typically stride != width.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • Thanks for pointing this out - thats was really helpful. Also, as I've figured out - strides in term of working sws_scale algo is a value of ammount of samples to iterate to be on the next row below current position. Using this guess I think its possible to use negative values as strides values to make some mirroring or final image flip in place as well. – armigero Aug 15 '18 at 18:02
1

Thanks to @VTT for pointing out the possible problem - I have fixed destination slice pointers calculation to the following:

    int dest_x = 200, dest_y = 70;

    //into 100x100 position
    std::int32_t y_offset = current_frame.hdr.width * dest_y + dest_x;
    std::int32_t u_offset = ( current_frame.hdr.width * dest_y )  / 4 + dest_x /2;
    std::int32_t v_offset = u_offset + y_plane_sz / 4;

    uint8_t* const dst_slice[] = {
        &current_frame.raw_frame[0] + y_offset,
        &current_frame.raw_frame[0] + y_plane_sz + u_offset,
        &current_frame.raw_frame[0] + y_plane_sz + v_offset,
        nullptr};

And the second problem with the "line artefact" is solved by using scaled dimensions sizes factor by 8.

One more addition for proper position calculations for destination slice pointers - that y coordinates must be re-adjust according to a current Y plane pointing because per each two Y strides there is only one U or V stride. For example (see adjusted_uv_y variable):

std::int32_t adjusted_uv_y = dest_y % 2 == 0 ? dest_y : dest_y - 1;
std::int32_t y_offset = current_frame.hdr.width * dest_y + dest_x;
std::int32_t u_offset = ( current_frame.hdr.width * adjusted_uv_y )  / 4 + dest_x /2;
std::int32_t v_offset = u_offset + y_plane_sz / 4;
armigero
  • 73
  • 6
  • armigero, could you describe what do you mean by "And the second problem with the "line artefact" is solved by using scaled dimensions sizes factor by 8"? I have the same problem. – denn Mar 21 '21 at 14:41
  • its really hard to recall this one and most of the pointers math for YUV420P manipulation is listed here. Could please elaborate what exactly problem do you have? Only "line artefact" or shifted colours as well? meanwhile I'll try to figure out this "factor 8" mention – armigero Mar 23 '21 at 18:05
  • I figured out the "line artefact" is present when the dimensions of the scaled frame don't divide by 8 without reminder. I think you mentioned that in your solution. Thank you for that. – denn Mar 24 '21 at 19:50