[algorithm] 최상의 이미지 축소 알고리즘 (품질 측면)은 무엇입니까?
래스터 그림의 크기를 줄이는 데 사용할 수있는 가장 좋은 알고리즘을 찾고 싶습니다. 가장 좋은 결과를 제공하는 것을 의미합니다. 바이 큐빅에 대해 알고 있지만 아직 더 나은 것이 있습니까? 예를 들어 Adobe Lightroom에는 내가 사용하던 표준 바이 큐빅보다 더 나은 결과를 생성하는 일종의 독점 알고리즘이 있다는 이야기를 들었습니다. 안타깝게도이 알고리즘을 소프트웨어에서 직접 사용하고 싶기 때문에 Adobe의 신중하게 보호되는 영업 비밀은 사용하지 않습니다.
추가 :
Paint.NET을 확인했는데 놀랍게도 사진을 축소 할 때 Super Sampling이 bicubic보다 나은 것 같습니다. 그래서 보간 알고리즘이 전혀 갈 길이 아닌지 궁금합니다.
또한 내가 “발명”했지만 구현 한 적이없는 알고리즘을 떠올리게했습니다. 나는 그것도 이름을 가지고 있다고 생각한다. 슈퍼 샘플링이 가장 가까운 것입니다.
아이디어는 이것이다-대상 그림의 모든 픽셀에 대해 소스 그림의 위치를 계산합니다. 아마도 하나 이상의 다른 픽셀을 오버레이 할 것입니다. 그런 다음 이러한 픽셀의 영역과 색상을 계산할 수 있습니다. 그런 다음 대상 픽셀의 색상을 얻기 위해 단순히 이러한 색상의 평균을 계산하고 해당 영역을 “가중치”로 추가합니다. 따라서 대상 픽셀이 노란색 소스 픽셀의 1/3과 녹색 소스 픽셀의 1/4을 덮는다면 (1 / 3 * yellow + 1 / 4 * green) / (1 / 3 + 1/4).
이것은 당연히 계산 집약적이지만 가능한 한 이상에 가까워 야합니다.
이 알고리즘의 이름이 있습니까?
답변
안타깝게도 원래 설문 조사에 대한 링크를 찾을 수 없지만 할리우드 촬영 감독이 영화에서 디지털 이미지로 이동하면서이 질문이 많이 나왔기 때문에 누군가 (아마도 SMPTE 일 수도 있고 ASC 일 수도 있음)는 여러 전문 촬영 감독을 모아서 영상을 보여주었습니다. 다양한 알고리즘을 사용하여 재조정되었습니다. 그 결과 거대한 영화를 보는이 전문가들에게는 Mitchell (고품질 Catmull-Rom이라고도 함)이 확장에 가장 적합하고 sinc 가 축소에 가장 적합 하다는 합의가있었습니다 . 그러나 sinc는 무한대로 이동하여 완전히 구현할 수없는 이론적 필터이므로 실제로 ‘sinc’가 무엇을 의미하는지 모르겠습니다. 아마도 sinc의 잘린 버전을 가리킬 것입니다. Lanczos는 단순히 자르는 것만으로도 개선을 시도하는 몇 가지 실용적인 sinc 변형 중 하나이며 스틸 이미지를 축소하기위한 최상의 기본 선택 일 것입니다. 그러나 평소처럼 이미지와 원하는 항목에 따라 다릅니다. 예를 들어 선을 유지하기 위해 선화를 축소하는 것은 꽃 사진을 축소 할 때 환영받지 못하는 가장자리를 유지하는 데 중점을 두는 경우입니다.
Cambridge in Color 의 다양한 알고리즘 결과에 대한 좋은 예가 있습니다.
fxguide 의 사람들은 살펴볼 가치 가 있는 스케일링 알고리즘에 대한 많은 정보 (합성 및 기타 이미지 처리에 대한 다른 많은 정보 와 함께) 를 모았 습니다 . 또한 자체 테스트를 수행하는 데 유용 할 수있는 테스트 이미지도 포함됩니다.
이제 ImageMagick은 리샘플링 필터에 대한 광범위한 가이드 를 제공합니다.
이미지를 축소하는 것에 대해 더 많은 논란이 있다는 것은 아이러니합니다. 이론적으로는 정보를 버리는 것뿐이기 때문에 완벽하게 수행 할 수있는 것입니다. t 존재합니다. 그러나 Lanczos부터 시작하십시오.
답변
이 에는 Lanczos 샘플링 바이 큐빅보다 느리지 만 높은 품질의 이미지를 생성합니다.
답변
(Bi-) linear 및 (bi-) cubic 리샘플링은 1/2보다 작은 요소로 축소 할 때 추악 할뿐만 아니라 끔찍하게 부정확합니다. 1/2 배로 다운스 캠핑 한 다음 가장 가까운 이웃 다운 샘플링을 사용하면 얻을 수있는 것과 유사한 매우 나쁜 앨리어싱이 발생합니다.
개인적으로 나는 대부분의 다운 샘플링 작업에 (면적) 평균화 샘플을 권장합니다. 매우 간단하고 빠르며 거의 최적입니다. 가우스 리샘플링 (반경이 계수의 역수에 비례하여 선택됨, 예를 들어 1/5로 다운 샘플링하는 경우 반경 5)은 더 많은 계산 오버 헤드로 더 나은 결과를 제공 할 수 있으며 수학적으로 더 건전합니다.
가우시안 리샘플링을 사용하는 한 가지 가능한 이유는 대부분의 다른 알고리즘과 달리 리샘플링 계수에 적합한 반경을 선택하는 한 업 샘플링과 다운 샘플링 모두에 대해 올바르게 작동한다는 것입니다 (아티팩트 / 앨리어싱을 도입하지 않음). 그렇지 않으면 두 방향을 모두 지원하려면 다운 샘플링을위한 영역 평균화 (업 샘플링을 위해 가장 가까운 이웃으로 저하됨)와 업 샘플링을 위해 (bi-) cubic (다운 샘플링을 위해 가장 가까운 이웃으로 저하됨)과 같은 두 개의 별도 알고리즘이 필요합니다. 가우시안 리샘플링의이 멋진 속성을 수학적으로 보는 한 가지 방법은 반경이 매우 큰 가우스가 면적 평균에 가깝고, 반경이 매우 작은 가우스가 (이중) 선형 보간에 가깝다는 것입니다.
답변
얼마 전에 Slashdot에서 Seam Carving 에 대한 기사를 보았는데 살펴볼 가치가있을 것입니다.
Seam 조각은 Shai Avidan과 Ariel Shamir가 개발 한 이미지 크기 조정 알고리즘입니다. 이 알고리즘은 크기 조정이나 자르기가 아니라 중요도가 거의없는 이미지에서 픽셀을 지능적으로 제거 (또는 픽셀 추가)하여 이미지의 크기를 변경합니다.
답변
설명하는 알고리즘을 선형 보간이라고하며 가장 빠른 알고리즘 중 하나이지만 이미지에서 최고는 아닙니다.
답변
이 알고리즘의 이름이 있습니까?
문헌에서는 “상자”또는 “창”리샘플링이라고 할 수 있습니다. 실제로 생각하는 것처럼 계산 비용이 적게 듭니다.
또한 1/2보다 많이 다운 샘플링 될 때 앨리어싱을 방지하기 위해 쌍 입방 보간에서 나중에 사용되는 중간 비트 맵을 만드는 데 사용할 수도 있습니다.
답변
관심있는 사람이 있다면 영역 평균 스케일링 알고리즘의 C ++ 구현입니다.
void area_averaging_image_scale(uint32_t *dst, int dst_width, int dst_height, const uint32_t *src, int src_width, int src_height)
{
// 1. Scale horizontally (src -> mid)
int mid_width = dst_width,
mid_height = src_height;
float src_width_div_by_mid_width = float(src_width) / mid_width;
float mid_width_div_by_src_width = 1.f / src_width_div_by_mid_width;
std::vector<uint32_t> mid(mid_width * mid_height);
for (int y=0; y<mid_height; y++)
for (int x=0; x<mid_width; x++)
for (int c=0; c<4; c++) {
float f = x * src_width_div_by_mid_width;
int i = int(f);
float d = ((uint8_t*)&src[i + y*src_width])[c] * (float(i) + 1 - f);
float end = f + src_width_div_by_mid_width;
int endi = int(end);
if (end - float(endi) > 1e-4f) {
assert(endi < src_width);
d += ((uint8_t*)&src[endi + y*src_width])[c] * (end - float(endi));
}
for (i++; i < endi; i++)
d += ((uint8_t*)&src[i + y*src_width])[c];
int r = int(d * mid_width_div_by_src_width + 0.5f);
assert(r <= 255);
((uint8_t*)&mid[x + y*mid_width])[c] = r;
}
// 2. Scale vertically (mid -> dst)
float mid_height_div_by_dst_height = float(mid_height) / dst_height;
float dst_height_div_by_mid_height = 1.f / mid_height_div_by_dst_height;
for (int y=0; y<dst_height; y++)
for (int x=0; x<dst_width; x++)
for (int c=0; c<4; c++) {
float f = y * mid_height_div_by_dst_height;
int i = int(f);
float d = ((uint8_t*)&mid[x + i*mid_width])[c] * (float(i) + 1 - f);
float end = f + mid_height_div_by_dst_height;
int endi = int(end);
if (end - float(endi) > 1e-4f) {
assert(endi < mid_height);
d += ((uint8_t*)&mid[x + endi*mid_width])[c] * (end - float(endi));
}
for (i++; i < endi; i++)
d += ((uint8_t*)&mid[x + i*mid_width])[c];
int r = int(d * dst_height_div_by_mid_height + 0.5f);
assert(r <= 255);
((uint8_t*)&dst[x + y*dst_width])[c] = r;
}
}