2012-11-13 6 views
3

기계의 고해상도 입력 데이터로 사용되는 거대한 TIFF 이미지 (회색조, 8 또는 16 비트, 최대 4GB)를 처리 중입니다. 각 이미지는 시계 방향으로 90도 회전해야합니다. 입력 TIFF는 LZW이거나 비 압축 일 수 있으며, 출력은 압축되지 않을 수 있습니다.거대한 TIFF를 90도 회전하는 속도를 높이려면 어떻게해야합니까?

지금까지 거대한 파일을 처리 할 수있는 Objective C (LZW 압축 풀기 포함)에서 자체 TIFF 판독기 클래스를 구현했으며 메모리에서도 일부 캐싱을 수행했습니다. 현재 TIFF 리더 클래스는 이미지 내부의 시각화 및 측정에 사용되며 매우 뛰어납니다.

TIFF를 회전시키려는 최근의 도전 과제에 대해서는 현재 구현이 매우 느리기 때문에 새로운 접근 방식이 필요합니다. "중간"크기의 TIFF (30.000 x 4.000)의 경우에도 약. 이미지를 회전하려면 30 분. 순간에 나는 모든 픽셀을 반복하고 반전 된 x와 y 좌표로 하나를 선택하고, 모든 라인을 버퍼에 넣고 한 라인이 완성 되 자마자 버퍼를 디스크에 씁니다. 주요 문제는 TIFF에서 읽는 것입니다. 데이터가 스트립으로 구성되어 있고 파일 내에서 선형으로 배포되지 않을 수 있습니다 (LZW 압축 스트립의 경우에는 선형도 없습니다).

필자의 소프트웨어를 프로파일 링하여 메모리 블록 (memmove)을 복사하는 데 많은 시간을 소비하고 회전을 위해 리더 클래스 내부에서 캐싱을 우회하기로 결정했습니다. 이제는 전체 프로세스가 약 5 % 빨라졌고 너무 많이는 아니며 항상 모든 시간이 fread() 내부에 소비됩니다. 최소한 캐시는 시스템의 fread() 캐시와 거의 비슷하게 수행된다고 가정합니다.

30,000 x 4.000 파일을 가진 Image Magick을 사용한 또 다른 테스트에는 완료하는 데 약 10 초 밖에 걸리지 않았습니다. AFAIK Image Magick은 전체 파일을 메모리로 읽어 들여 메모리에서 처리 한 다음 디스크에 다시 씁니다. 이것은 최대 수백 메가 바이트의 이미지 데이터까지 잘 작동합니다.

내가 찾고있는 것은 픽셀 데이터를 처리하는 다른 접근법과 같은 일종의 "메타 최적화"입니다. 픽셀을 하나씩 바꾸는 것 (그리고 서로 멀리있는 파일 위치에서 읽을 필요가있는)보다 전략이 있습니까? 프로세스 속도를 높이기 위해 중간 파일을 만들어야합니까? 어떤 제안이라도 환영합니다.

+0

글쎄, 내가 생각할 수있는 최선의 * 메타 최적화는 이미지를 회전시키지 않고 회전하지 않는 형식으로 작업하는 것입니다. 이미지를 회전하는 것보다 다운 스트림 프로세스가 작동하는 방식을 조정하여 성능을 향상시킬 수 있습니다.나는 당신이 이미 이것을 고려하고 그것을 거부했기를 기대합니다. 나는 이미 회전 된 이미지를 캡쳐하는 것을 고려하고 거부 할 것을 기대합니다 (카메라를 옆으로 돌리거나 무엇이든). –

+0

불행히도 수신 컴퓨터는 블랙 박스이며 회전 된 형태로만 데이터를 받아들이는 반면 설계 프로세스는 이미지를 회전하지 않는 방식으로 뱉어냅니다. 이 둘 사이를 연결해야하며 데이터 자체에는 영향을주지 않아야합니다. – nullp01nter

+0

나는 당신이 아마 오래 전에 풀었던 낡은 질문을 안다. 그러나 이런 종류의 일을 위해 고안된'VIPS'를 살펴볼 수는있다 ... http://www.vips.ecs.soton.ac. uk/index.php? title = VIPS –

답변

2

픽셀 밍을 수행해야한다고 가정하면 전체적인 문제를 살펴 보겠습니다. 30000x4000 픽셀의 중간 이미지는 8 비트 회색의 경우 이미지 데이터가 120M이고 16 비트 이미지 데이터가 240M입니다. 따라서이 방법으로 데이터를 살펴 본다면 "30 분이면 합리적입니까?"라고 물어볼 필요가 있습니다. 90도 회전을 수행하기 위해서는 최악의 문제, 즉 메모리를 유발할 수 있습니다. 행 하나를 채우기 위해 단일 열의 모든 픽셀을 터치하고 있습니다. 행렬로 작업한다면 최소한 메모리 발자국을 두 배로 늘리지는 않을 것입니다.

픽셀의 120M은 120M 읽기 및 120M 쓰기 또는 240M 데이터 액세스를하는 것을 의미합니다. 이것은 초당 대략 66,667 픽셀을 처리하고 있다는 것을 의미합니다. 너무 느린 것 같습니다. 제 생각에는 적어도 초당 120 만 화소의을 처리해야한다고 생각합니다.

이것이 저라면, 프로파일 링 도구를 실행하고 병목 현상이 발생한 위치를 확인한 후이를 잘라 냈습니다. 정확한 구조를 알고 생각하지 않고

, 나는 다음을 수행합니다 : 소스 이미지의 메모리 하나 개의 연속 블록을 사용하는

시도를

나는 회전을 볼 원합니다 함수는 다음과 같습니다 :

void RotateColumn(int column, char *sourceImage, int bytesPerRow, int bytesPerPixel, int height, char *destRow) 
{ 
    char *src = sourceImage + (bytesPerPixel * column); 
    if (bytesPerPixel == 1) { 
     for (int y=0; y < height; y++) { 
      *destRow++ = *src; 
      src += bytesPerRow; 
     } 
    } 
    else if (bytesPerPixel == 2) { 
     for (int y=0; y < height; y++) { 
      *destRow++ = *src; 
      *destRow++ = *(src + 1); 
      src += bytesPerRow; 
      // although I doubt it would be faster, you could try this: 
      // *destRow++ = *src++; 
      // *destRow++ = *src; 
      // src += bytesPerRow - 1; 
     }    
    } 
    else { /* error out */ } 
} 

저는 루프의 내부가 아마도 8 개의 명령어로 바뀔 것이라고 생각합니다. 2GHz 프로세서에서 (명 령적으로 명령 당 4 사이클, 이는 추측 일뿐입니다), 초당 6 억 2 천 5 백만 픽셀을 회전시킬 수 있어야합니다. 대충.

연속적으로 수행 할 수없는 경우 한 번에 여러 대상 스캔 라인에서 작업하십시오.

소스 이미지가 블록으로 분할되거나 메모리의 추상화 된 스캔 라인이있는 경우 원본 이미지에서 스캔 라인을 가져 와서 한 번에 몇 개의 열을 대상 스캔 라인의 버퍼로 회전해야합니다.

스캔 라인을 획득하고 릴리스 할 수있는 추상적으로 스캔 라인에 액세스하는 메커니즘이 있다고 가정 해 보겠습니다. 이 경우

void RotateNColumns(Pixels &source, Pixels &dest, int startColumn, int nCols) 
{ 
    PixelRow &rows[nRows]; 
    for (int i=0; i < nCols; i++) 
     rows[i] = dest.AcquireRow(i + startColumn); 

    for (int y=0; y < source.Height(); y++) { 
     PixelRow &srcRow = source.AcquireRow(); 
     for (int i=0; i < nCols; i++) { 
      // CopyPixel(int srcX, PixelRow &destRow, int dstX, int nPixels); 
      sourceRow.CopyPixel(startColumn + i, rows[i], y, 1); 
     } 
     source.ReleaseRow(srcRow); 
    } 

    for (int i=0; i < nCols; i++) 
     dest.ReleaseAndWrite(rows[i]); 
} 

: 당신이 코드는 다음과 같이 보일 것이기 때문에

그런 다음 당신이해야 할 겁니다 것은, 당신이 한 번에 처리 할 의사가있는 많은 소스 열 알아낼 스캔 라인의 큰 블록에 원본 픽셀을 버퍼링하면 반드시 힙을 조각 내고있는 것은 아니며 디코딩 된 행을 디스크로 플러시 할 수 있습니다. 한 번에 n 개의 열을 처리하고 메모리 지역성을 n 배로 향상시켜야합니다. 그렇다면 캐싱이 얼마나 비싼 지에 대한 질문이됩니다.

병렬 처리로 문제를 해결할 수 있습니까?

정직하게 말하자면, 귀하의 문제는 CPU 바인딩이 아닌 IO 바인딩이어야한다고 생각합니다. 나는 당신의 디코딩 시간이 지배적이라고 생각할 것이다. 그러나 그것이 웃지 않는 척하지 말자.

원본 이미지를 한 번에 전체 행을 읽으면 디코딩 된 행을 대상 이미지의 해당 열에 쓰는 스레드로 던질 수 있습니다. 따라서 OnRowDecoded (byte * row, int y, int width, int bytesPerPixel)와 같은 메서드를 갖도록 디코더를 작성하십시오. 그리고 나서 해독하는 동안 회전하고 있습니다. OnRowDecoded()는 정보를 압축하여 대상 이미지를 소유 한 스레드로 전달하고 디코딩 된 전체 행을 올바른 대상 열에 씁니다. 이 스레드는 주 스레드가 다음 행을 디코딩하는 동안 사용중인 모든 쓰기를 수행합니다. 작업자 스레드가 먼저 완료되지만 가능하지 않을 수 있습니다.

destPermel()을 스레드로부터 안전하게 만들 필요가 있지만 그 외의 경우에는 직렬 작업이어야합니다. 실제로 원본 이미지에서 TIFF 기능을 사용하여 밴드 나 타일로 나눠서 사용하는 경우이를 병렬로 해독하거나 할 수 있습니다.

+0

내 주제에 대한 많은 생각을 해주셔서 감사합니다. 전체 이미지를 메모리로 읽어들이는 것이 항상 가능한 것은 아니며 원본 데이터가 라인 버퍼링됩니다. 원본 데이터를 가져 오는 것이 쓰기보다 비용이 많이 들기 때문에 가능한 많은 입력 라인을 유지하기 위해 버퍼 (256MB)를 구현하기로 결정한 다음이 라인을 열 방향으로 스캔하여 출력 라인의 단편을 구성합니다. 디스크. 이미지가 해당 버퍼에 맞으면 회전이 거의 즉시 수행됩니다. 그렇지 않은 경우 쓰기 작업은 많은 작은 패킷으로 분할됩니다. – nullp01nter

+0

대용량 입력 파일에 대한 코드를 프로파일 링 했으므로 이제는 약입니다. 50 배 빠릅니다 (내 버퍼보다 ​​큰 파일의 경우). 이것은 큰 성과입니다. 그러나 속도가 크게 높아질 수 있습니다 : fseek()를 사용하여 다음 조각을위한 파일 포인터를 배치해야 할 때마다 시스템의 쓰기 기능 (MacOS에서 사용)에 많은 처리 시간이 소요됩니다. 서로 연결하십시오 (이미지가 버퍼에 맞을 경우). 이 경우에 fseek()를 제거하면 연속 fwrite()가 훨씬 빠르게 수행되므로 나에게 분명하지 않습니다. 이견있는 사람? – nullp01nter

+0

확실히 - OS가 쓰기를 버퍼링하고 있습니다. fseek()를 수행하면 OS가 플러시되어 데이터가 손상되지 않았는지 확인합니다. 나는 다른 OS에서도 이것을 보았다. 파일 포인터가 현재 위치와 다른 위치에있을 때만 찾기를 호출하는 fseek()를 감싸는 대부분의 경우 개선 된 점을 발견했습니다. – plinth

1

TIFF 사양을 보면 image orientation을 설정하는 이미지 IFD에 추가 할 수있는 태그가 있습니다. 이 태그를 적절하게 설정하면 이미지를 디코딩하고 다시 인코딩 할 필요없이 이미지 회전을 변경할 수 있습니다.

그러나 -, 생태계의 모든 탈선 TIFFs을 처리하는 것은 확실히 아닌 사소한 당신은 정직 표시되는 동안, 사소한하지 않으면 것은 TIFF에서 IFDs을 다시 작성하는 것을 알고 있어야한다 - 이것은 그러나 입니다 그래서 당신이 어떻게 그것에 대해 가는지주의하십시오.

+0

위에서 언급 한 것처럼 (두 번째 설명) 단순히 플래그를 설정하는 것만으로는 충분하지 않습니다. 대상 컴퓨터는 파일의 Y 축을 따라 빠르게 처리되기 때문에 데이터를 재구성해야합니다 (높은 속도로 레이저를 작동시키는 것이 포함됩니다). 대상 컴퓨터는 단순히 파일을 임의의 위치로 건너 뛰고 이미지 데이터를 읽을 시간이 충분하지 않기 때문에 데이터를 선형 스트림으로 제공해야합니다. 또한 TIFF IFD의 복잡성을 알고 있으며 라이브러리를 사용하여 IFD를 구문 분석하고 IFD를 만드는 등 TIFF를 가져오고 내보낼 수 있습니다. – nullp01nter

관련 문제