빠른 2:1 이미지 축소(축소)

2022. 12. 14. 19:06프로그래밍

728x90

이 문서에는 2:1 이미지 축소 알고리즘의 빠른 구현이 포함되어 있습니다.

 

소개

이 기사는 2:1 이미지 축소 알고리즘의 간단하고 빠르고 실용적인 구현을 제공합니다.

2:1 이미지 축소는 매우 특정한 경우에 사용되지만 멋진 작업이기도 하죠.

 

배경

이미지 처리는 항상 비용이 많이 드는 작업입니다. 크기 조정된 그림을 표시해야 하는 경우 렌더링에 GPU(OpenGL, DirectX)를 사용하는 것이 항상 더 좋습니다. 하지만 GPU 사용이 항상 가능한 것은 아니기때문에 대안 하나 있는 것도 좋을 것입니다.

 

고품질 이미지 크기 조정(확대/축소)은 특히 큰 이미지에서 CPU를 많이 사용합니다. 게다가 여러 번 프로세싱해야 하는 경우도 있습니다. 이 과정을 줄여주기 위해 적용할 수 있는 다양한 트릭이 존재 합니다. 그림을 축소(축소)해야 하는 경우 밉맵과 유사한 트릭을 적용할 수 있을 것입니다. 이러한 경우는 사진을 두 배 이상 축소하려는 경우(대상 크기가 원본의 절반 미만) 실제 크기를 조정하기 전에 half-shrink(반 축소)를 적용하여 전체 프로세스의 속도를 높일 수 있을 것입니다. 2:1 축소의 경우 더 적은 계산을 사용하는 가장 간단한 알고리즘으로 인해 매우 빠릅니다.

 

예를 들어 1920x1024 이미지를 우리가 가지고 있고 이 이미지를 500x281로 축소해야 하는 경우 실행 해야할 코드는 다음의 것과 같을 것입니다:

shrink( imageDst, imageSrc )
 

그러나 우리는 다음과 같이 사전 축소 2:1로 이를 최적화할 수 있을 것입니다:

shrinkHalf( imageHalf, imageSrc );
shrink( imaheDst, imageHalf );
 

내 프로젝트 중 하나에서 미리 보기를 생성하기 위해 3개의 모니터 이미지로 확장해야 했을 때 결과는 다음과 같습니다:

Scale: 46 ms
ShrinkHalf + Scale: 25 ms
ShrinkHalf + ShrinkHalf + Scale: 16 ms
 

즉, 트릭을 한 번 적용하면 36% 정도 속도가 향상 된다는 것입니다.

그리고 트릭을 두 번 적용하면 66% 속도 향상이 발생합니다.

 

데모 애플리케이션에는 두 개의 버튼이 있습니다:

  • Shrink 2:1 - 전체 소스 이미지의 이미지 축소를 수행합니다.
  • Shrink 2:1 rnd - 원본 이미지에서 임의의 사각형 이미지 축소 수행

 

왼쪽에 원본 이미지가 있고, 오른쪽에는 회색조의 원본 이미지와 컬러로 축소된 이미지(또는 축소된 부분)가 있습니다.

 

원본이 업데이트되고 축소된 사본도 업데이트해야 하는 경우 이미지의 일부만 축소하는 것이 적절합니다. 이렇게 하면 처리를 위해 많은 CPU와 시간을 절약할 수 있습니다.

 

전체 그림의 2:1

임의 직사각형의 2:1

코드 사용해 보기

프로젝트에는 다음의 한 쌍의 파일이 존재 합니다: ShrinkHalf.h 및 ShrinkHalf.cpp

코드를 사용하려면 이러한 파일을 프로젝트에 넣고 ShrinkHalf.h 파일을 포함하기만 하면 됩니다.

이 파일에 알고리즘의 전체 구현이 포함되어 있습니다.

 

모든 함수들이 받는 인수는 다음 것들입니다:

 

  • BYTE* 픽셀 - 24비트 BGR 형식의 픽셀 배열(b,g,r, b,g,r, ..., b,g,r)
  • int width - 이미지의 너비(픽셀 단위)
  • int height - 이미지의 픽셀 높이

따라서 함수들을 사용하려면 다음과 같이 해야만 합니다:

#include "ShrinkHalf.h"

...

// This will convert the color image to black/white using 16x16 matrix
shrinkHalf( dstPixels, srcPixels, srcWidth, srcHeight );
 

대상 이미지는 소스 크기에서 계산되므로 차원을 제공하지 않음에 주의 하십시오.

사용 가능한 함수들은 다음과 같습니다:

/////////////////////////////////////////////////////////////////////////////
//    Fast and smooth 2:1 image resize
/////////////////////////////////////////////////////////////////////////////

void    shrinkHalf    ( BYTE* target, const BYTE* source, int srcWidth, int srcHeight );
void    shrinkHalfPart( BYTE* target, const BYTE* source, int srcWidth, 
                        int srcHeight, int x1, int y1, int x2, int y2 );

/////////////////////////////////////////////////////////////////////////////
 
 

첫 번째 함수는 전체 이미지를 2:1 축소합니다.

두 번째 함수는 원본 이미지에서 지정된 사각형의 2:1 축소를 수행하여 대상 이미지에 대한 각 사각형을 계산합니다.

이것을 사용하는 경우 정수 반올림으로 인해 원본 사각형 포인트의 +1/-1 수정이 필요할 수 있다는 점에 주의해야 합니다.

적용해야 하는 집중적인 업데이트(예: 비디오 렌더링)가 있는 경우 이 기법은 매우 유용합니다.

 

주요 관점들

shrinkHalf 함수에는 동작하는 코드를 주석 처리 하였지만, 나는 shrinkHalfPart에 대한 내부 호출을 그대로 두는 것을 선호했습니다.

//static
void    shrinkHalf( BYTE* target, const BYTE* source, int srcWidth, int srcHeight )
{
    shrinkHalfPart( target, source, srcWidth, 
                    srcHeight, 0, 0, srcWidth-1, srcHeight-1 );

    //~~~~~
/*
    int    dstWidth    = srcWidth  / 2;
    int    dstHeight   = srcHeight / 2;

    int    srcLineBytes    = srcWidth * 3;
    int    dstLineBytes    = dstWidth * 3;
    int    dstRows         = dstHeight;

    const BYTE*    sl    = source;
          BYTE*    tl    = target;
    const BYTE*    te    = target + srcHeight/2 * dstLineBytes;

    while( tl < te )
    {
              BYTE*    pt    = tl;
        const BYTE*    p1    = sl;
        const BYTE*    p2    = p1 + srcLineBytes;
        const BYTE*    pe    = sl + srcLineBytes;

        while( p1 < pe )
        {
            *pt++    = (p1[0] + p1[3] + p2[0] + p2[3]) >> 2;    //    / 4;    blue;
            *pt++    = (p1[1] + p1[4] + p2[1] + p2[4]) >> 2;    //    / 4;    green;
            *pt++    = (p1[2] + p1[5] + p2[2] + p2[5]) >> 2;    //    / 4;    red;

            p1    += 6;//2*3;
            p2    += 6;//2*3;
        }

        sl    += srcLineBytes << 1;    //    Shift by 1 is equal to " * 2 "
        tl    += dstLineBytes;
    }
*/
}

따라서 누군가가 깨끗하고 간단한 함수를 원하는 경우 shrinkHalfPart를 호출하는 첫 번째 줄을 제거하고 주석 처리된 코드의 주석을 제거 하면 됩니다.

 

History

  • 8th April, 2020: Initial version

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

 

 

이상.

 

728x90