Halide를 사용하여 Android에서 빠른 이미지 처리하는 방법
나는 Halide를 사용하여 빠르고 유지 관리 가능한 코드를 작성하는 방법에 대해 썼습니다. 이것은 Android 애플리케이션으로 그 위력을 보여줄 것입니다.
Halide는 최신 시스템에서 고성능 이미지 처리 또는 배열 처리 코드를 더 쉽게 작성하고 유지 관리할 수 있도록 설계된 오픈 소스 도메인 특정 언어입니다.
나는 Halide에 대한 시리즈를 작성해 왔으며 이 기사는 시리즈의 3번째 기사입니다. 지난 두 글에서 이야기한
이 기사에서는 Android에서 Halide를 사용하는 방법과 Halide가 제공할 수 있는 성능 향상에 대해 쓸 것입니다.
성능상의 이점을 보여주기 위해 다음의 문제 설명을 재사용할 것입니다. YUV
에게 RGB
색상 형식 변환. 나는 과거에 이 예제를 사용하여 이 예제를 사용하여 Android에서 이미지 처리를 수행하는 다양한 방법을 보여주는 몇 가지 기사를 작성했습니다.
면책 조항: 이 기사에서 언급된 모든 의견은 제 의견이며 제가 함께 일하는 조직의 의견이나 입장을 반영하지 않습니다.
저는 Google의 카메라에서 HDR 모드 및 야간 모드와 같은 기능을 작업합니다. 어려운 문제입니다. 리소스가 제한된 장치에서 복잡한 알고리즘으로 고해상도 이미지를 처리해야 합니다. Halide가 정말 빛나는 곳입니다!
Android에서 Halide를 사용하는 방법을 배우는 데 전적으로 관심이 있는 경우 이 하위 섹션을 건너뛸 수 있습니다.

문제는 8MP(8 메가 픽셀 = ~ 8,000,000 픽셀) 이미지를 하나의 평면을 갖는 YUV_420_888이라는 특정 형식으로 변환하는 것입니다. Y
채널 및 2개의 반평면 서브샘플링 UV
Android의 Bitmap에서 일반적으로 지원되는 ARGB_8888 형식으로 채널을 변환합니다. Wikipedia에서 YUV 형식에 대해 자세히 알아볼 수 있습니다. 또한 아래 기사에는 문제 설명이 더 잘 설명되어 있습니다.
내가 이것을 문제 진술로 선택한 이유는 YUV_420_888
Android Camera API에서 지원하는 가장 일반적인 OUTPUT 형식 중 하나이며 이미지는 일반적으로 다음과 같이 사용됩니다. Bitmap
Android에서 — 따라서 이것을 상당히 일반적인 문제 설명으로 만듭니다.
저는 Android에서 이미지 처리 알고리즘이 어떻게 수행되는지 이해하기 위해 다양한 프레임워크/기술의 성능을 실험해 왔습니다. 나는 그것들을 서로 비교하기 위해 동일한 문제 진술을 사용했습니다. 다음은 내가 테스트한 다른 기술을 사용하여 동일한 몇 가지 예입니다.
Pixel 4A 기기에서 실행하여 위에 나열된 다양한 솔루션으로 문제 설명에 대해 다음 번호를 기록했습니다.
지금까지, RenderScript
기반 접근이 가장 빠른 것으로 관찰되었다. 그러나 RenderScript는 Android 12부터 더 이상 사용되지 않습니다. 여기에서 자세한 내용을 읽을 수 있습니다. 개발 팀은 새 하드웨어에서 훨씬 더 성능이 좋을 것으로 예상되는 몇 가지 대안을 공유했습니다.
다음 섹션에서는 이 문제에 대한 Halide 기반 솔루션을 공유하고 이 접근 방식을 사용한 벤치마크 결과를 살펴보겠습니다.
이전 기사 Halide에서 언급했듯이 일정에서 알고리즘을 분리할 수 있습니다. 먼저 YUV에서 RGB로 변환하는 알고리즘을 살펴보겠습니다.
이 경우 입력 이미지 형식이 YUV_420_888이라고 가정합니다. 이 이미지 형식의 몇 가지 주요 측면은
Luma 채널(Y 채널)은 전체 해상도 및 평면입니다.
- 이는 Y-평면이 U/V 평면과 인터리브되지 않도록 보장됨을 의미합니다.
크로마 채널(UV 채널)은 서브샘플링되고 인터리브될 수 있습니다.
- 서브샘플링은 4개의 Y 픽셀에 대해 하나의 UV 픽셀이 있음을 의미합니다.
- 인터리브를 통해 UV 데이터를
UVUVUVUVUVUV
이미지의 각 행에 대한 메모리의 패턴입니다.
지금까지의 예에서, ARGB
출력에는 각 채널이 있습니다(R
또는 G
또는 B
또는 A = alpha
) 와 함께 uint8
단일에 저장된 데이터 int32
값. 우리는 계속 같은 일을 할 것입니다.
할로겐 생성기는 다음과 같이 보일 수 있습니다.
Halide에서 명시적인 일정을 작성하지 않으면 모든 것이 인라인으로 계산됩니다. 일정 작성에는 Halide, 대상 하드웨어 및 일정 수준의 적중 및 시험에 대한 전문 지식이 포함되는 경우가 많습니다. IMO, 첫 번째 단계는 항상 벤치마크를 작성하고 대상 하드웨어에서 실행하는 것이어야 합니다. 이를 위해 google/benchmark에서 오픈 소스 벤치마킹 프레임워크를 사용할 수 있습니다.
첫 일정부터 시작하자
기본 일정
이것은 각 픽셀에 대한 두 개의 for 루프 내에서 모든 계산을 인라인으로 수행합니다.
Pixel 4A에서 실행하면 위에서 언급한 생성기에 대해 다음과 같은 결과가 나타납니다.
이 숫자는 표준 네이티브 코드로 얻은 것과 유사합니다. 그러나 Halide로 짜낼 수 있는 숫자에 대한 기대치가 더 높습니다.
분할, 병렬화 및 벡터화
이러한 기본 요소에 대해 자세히 알아보려면 이 시리즈의 기사 2인 Halide 프로그래밍 언어의 일반 개념 이해를 참조하세요.
이 일정은 루프를 더 많은 부분으로 분할하고 명령을 벡터화합니다. xi
루프 및 병렬화 y
고리. 벤치마크 결과를 보자
팔! 기본 일정보다 엄청나게 빨라진 속도가 보이시나요?
시간을 줄이는 것과 같은 추가 최적화가 가능할 수 있습니다. uv_centered
계산되거나 다른 분할 요소를 시도하고 있지만 지금까지는 좋아 보입니다.
위에서 언급한 생성기는 다음과 같은 C++ 메서드를 생성합니다.
에서 직접 사용할 수 있는 C++
라이브러리 또는 JNI 코드.
앞으로 필요한 경우 Android 스튜디오에서 Halide를 설정하고 종단 간 사용하는 방법에 대해 쓸 것입니다. 댓글을 통해 도움이된다면 LMK.
모든 것을 함께 연결하려면 전체 엔드 투 엔드 파이프라인을 연결해야 합니다.
Java --> JNI --> Halide --> Java --> Bitmap
이는 전체 대기 시간으로 이어집니다. ~28ms
. 따라서 지금까지 고려한 다양한 접근법의 결과를 살펴보면
이것은 우리에게 고성능과 유지보수가 쉬운 코드를 제공합니다! 엔지니어링 팀이 원하는 것은 무엇입니까?
이 문제에 대해 훨씬 더 빠른(약 ~12ms의 대기 시간) 다른 솔루션을 찾았지만 하드웨어별 구현(NEON 내장 활용)과 병렬 처리 등을 모두 필요로 합니다. 작성하거나 유지 관리하는 것이 쉽지는 않지만 나중에 기사를 쓸 가치가 있습니다.
매우 효율적인 코드를 작성하는 사람들은 코드를 작성하는 데 소비하는 시간보다 코드 최적화에 최소 2배의 시간을 소비한다고 말합니다.
— 인터넷의 어떤 것
Halide를 사용하면 다양한 일정을 훨씬 쉽게 시도하고 조정할 수 있습니다. 루프 순서, 논리 분할, 스레딩 등을 수동으로 변경하는 것보다 쉽고 ABI 특정 벡터화된 코드를 작성하고 유지 관리하는 데 따른 고통을 제거합니다.
Halide, Auto vectorized C++ 코드와 같은 접근 방식은 이식 가능하고 유지 관리가 더 쉽습니다.
- 명시적으로 손으로 조정한 CPU 특정 코드와 비교하여
항상 최적화하기 전에 사용 사례를 고려하십시오.
- 예 — 라이브러리 개발자 대 앱 개발자
- 전체 작업에 2초가 걸리는 경우와 같은 실제 병목 현상을 이해하고
28 ms
그리고12 ms
큰 이점을 제공하지 않을 수 있습니다.
애플리케이션이 성능에 중요한 경우: 벤치마크 → 분석 → 최적화(반복)
'Coding' 카테고리의 다른 글
서버리스 웹 소켓 — 예제를 사용하는 방법과 이유 (0) | 2022.05.16 |
---|---|
Docker 컨테이너 간에 Postgres 소켓을 공유하는 방법 (0) | 2022.05.09 |
C++로 테스트 프레임워크를 구축하기 위한 5가지 필수 매크로 (0) | 2022.05.08 |
하나님은 CI/CD를 사용하십니까? (0) | 2022.05.07 |
Async / Await in Go 사용 방법 (0) | 2022.05.02 |
댓글