무한 비트 맵 [닫힘]


10

런타임 중에 비트 맵을 만들고 싶습니다. 비트 맵은 모든면에서 확장 가능해야하며 픽셀 액세스는 조용히 효율적이어야합니다.

일부 그림 http://img546.imageshack.us/img546/4995/maptm.jpg

그림에 표시된 명령 사이와 이후에 Map.setPixel () 및 Map.getPixel ()은 비트 맵에 저장된 데이터를 설정 / 반환해야합니다.

나는 setPixel () / getPixel이 가능한 한 빠르도록 메모리를 할당하는 방법에 대한 구현 개념을 기대하지 않습니다.


회색 필드가 항상 포인트 (0,0)입니까 아니면 다른 좌표 일 수도 있습니까?
팔콘

2
자세한 내용이 필요합니다. 설정된 픽셀이 희박합니까? 어떻게 천천히 당신은 할 기꺼이 extendX수 있도록하기 위해 방법을 setPixelgetPixel빠른 것들?
피터 테일러

1
비트 맵이 너무 커서 메모리에 맞지 않습니까? 빠른 작업, 확장, setPixel (), getPixel ()은 무엇입니까?

1
@Falcon : 아니요, 충분한 시간이 있습니다
SecStone

3
질문이 포함 된 이미지에 크게 의존하기 때문에이 질문을 주 제외로 닫으려고 투표하고 있습니다. 현재 쓰여진 것처럼, 이치에 맞지 않습니다.

답변:


9

경우 extend()작업이 합리적으로 빠른 있어야하는 쿼드 트리가 잘 맞는 수 있습니다; 실제로 명시적인 확장 작업이 필요하지 않습니다. 물론 개별 픽셀에 대한 임의 액세스에 대해 최적의 성능을 제공하지는 않지만 기본 동작은 픽셀을 반복 하는 것으로 쿼드 트리가 매트릭스 기반 구현만큼이나 빠른 속도로 수행 할 수 있습니다 (그리고 더 빠름). 반복이 항상 같은 방식으로 발생하지 않으면 매트릭스가 배치됩니다).

요구 사항은 실제로 Game of Life와 같은 셀룰러 오토 마톤을 구현하려는 것처럼 들립니다. 무한한 그리드에서 Game of Life를 구현할 수있는 매우 뛰어난 성능 인 Hashlife를 살펴볼 수 있습니다 . Quadtree를 기반으로하지만 게임 규칙의 지역성을 기반으로 매우 지능적인 추가 최적화를 수행합니다.


이 아이디어에 감사드립니다! 몇 가지 테스트를 수행하고 결과를보고합니다.
SecStone

2

@SecStone은 확장 작업에 사용할 수있는 시간이 충분하다고 말했기 때문에 픽셀을 저장하는 가장 쉽고 효율적인 방법은 단일 평면 배열 또는 2 차원 배열을 사용하는 것입니다. 픽셀은 일정한 시간에 액세스 할 수 있기 때문입니다.


4
확장이 어떻게 처리되어야한다고 생각하는지에 대해 좋은 제안을한다면이 투표에 참여할 것입니다.
Doc Brown

@ Doc Brown : 시간이 충분하면 배열을 이동하십시오. 또는 청크와 포인트 투 배열 및 청크 인덱스 (일정한 시간에도 실행)에 대한 변환기 기능으로 무언가를 해결할 수 있습니다.
팔콘

2

손으로

메모리가 매우 드문 리소스가 아닌 경우 더 큰 청크 작업을 고려합니다.
여기 의사 코드가 있습니다.

class Chunk {
    Chunk new(int size) {...}
    void setPixel(int x, int y, int value) {...}
    int getPixel(int x, int y) {...}
}

class Grid {
    Map<int, Map<Chunk>> chunks;
    Grid new(int chunkSize) {...}
    void setPixel(int x, int y, int value) {
         getChunk(x,y).setPixel(x % chunkSize, y % chunkSize, value);//actually the modulo could be right in Chunk::setPixel and getPixel for more safety
    }
    int getPixel(int x, int y) { /*along the lines of setPixel*/ }
    private Chunk getChunk(int x, int y) {
         x /= chunkSize;
         y /= chunkSize;
         Map<Chunk> row = chunks.get(y);
         if (row == null) chunks.set(y, row = new Map<Chunk>());
         Chunk ret = row.get(x);
         if (ret == null) row.set(x, ret = new Chunk(chunkSize));
         return ret;
    }
}

이 구현은 순진합니다.
우선 getPixel에 청크를 만듭니다 (기본적으로 해당 위치에 청크가 정의되어 있지 않은 경우 단순히 0 정도만 반환해도됩니다). 둘째, 충분히 빠르고 확장 가능한 Map 구현이 있다는 가정을 기반으로합니다. 내 지식에는 모든 괜찮은 언어가 하나 있습니다.

또한 청크 크기로 재생해야합니다. 고밀도 비트 맵의 ​​경우 큰 청크 크기가 좋고 희소 비트 맵의 ​​경우 작은 청크 크기가 좋습니다. 실제로 매우 드문 경우에는 "청크 크기"가 1이 가장 좋습니다. "청크"자체가 더 이상 사용되지 않고 데이터 구조를 int 픽셀 맵의 int 맵으로 줄입니다.

선반에서

다른 해결책은 일부 그래픽 라이브러리를 보는 것입니다. 실제로 하나의 2D 버퍼를 다른 2D 버퍼로 그리는 데 능숙합니다. 즉, 단순히 더 큰 버퍼를 할당하고 해당 좌표에 원본을 그립니다.

일반적인 전략으로서 : "동적으로 성장하는 메모리 블록"이있을 때, 일단 사용되면 그 배수를 할당하는 것이 좋습니다. 이것은 메모리를 많이 사용하지만 할당 및 복사 비용을 크게 줄 입니다. 대부분의 벡터 구현은 크기가 초과 될 때 크기를 두 배로 할당합니다. 따라서 특히 상용 솔루션을 사용하는 경우 하나의 픽셀 만 요청 되었기 때문에 버퍼를 1 픽셀 씩 확장하지 마십시오. 할당 된 메모리가 저렴합니다. 재 할당, 복사 및 해제는 비용이 많이 듭니다.


1

몇 가지 조언 만하면됩니다.

  • 이것을 정수 타입의 배열 (또는 배열의 배열)로 구현하면 복사 할 때 비트를 이동하지 않도록 매번 약간의 비트 / 픽셀만큼 배킹 배열을 늘려야합니다. 단점은 더 많은 공간을 사용하지만 비트 맵이 커질수록 낭비되는 공간의 비율이 감소한다는 것입니다.

  • 맵 기반 데이터 구조를 사용하는 경우 getPixeland setPixel호출 의 x, y 좌표 인수를 간단히 재배치하여 비트 맵 증가 문제를 해결할 수 있습니다 .

  • 맵 기반 데이터 구조를 사용하는 경우 "하나"에 대한 맵 항목 만 필요합니다. "0"은 항목이 없음을 나타낼 수 있습니다. 이는 특히 비트 맵이 대부분 0 인 경우 상당한 공간을 절약합니다.

  • 지도의지도를 사용할 필요는 없습니다. intx, y 쌍을 단일로 인코딩 할 수 있습니다 long. 비슷한 프로세스를 사용하여 배열 배열을 배열에 매핑 할 수 있습니다.


마지막으로, 3 가지 균형을 맞춰야합니다.

  1. 의 성능 getPixelsetPixel,
  2. 의 성능 extend*운영,
  3. 공간 활용.

1

더 복잡한 것을 시도하기 전에 메모리에 모든 것을 담을 수 없다면, 물건을 단순하게 유지하고 좌표계의 원점에 대한 정보와 함께 2 차원 배열을 사용하십시오. 이를 확장하려면 예를 들어 C ++ std :: vector does :와 같은 전략을 사용하십시오. 실제 배열 크기와 배열 용량을 구분하고 한계에 도달 할 때마다 청크 단위로 용량을 확장하십시오. 여기서 "용량"은 간격 (from_x, to_x), (from_y, to_y)로 정의해야합니다.

이것은 때때로 메모리의 완전한 재 할당이 필요할 수 있지만, 너무 자주 발생하지 않는 한, 목적에 따라 충분히 빠를 수 있습니다 (사실, 이것을 시도 / 프로파일해야합니다).


1

픽셀 액세스를 수행하는 가장 빠른 방법은 개별적으로 주소를 지정할 수있는 픽셀의 2 차원 배열입니다.

확장의 경우 매번 재 할당하고 복사하는 간단한 구현으로 시작하십시오 (어쨌든 해당 코드가 필요하기 때문에). 프로파일 링에 많은 시간을 소비하고 있다고 나타내지 않으면 더 세분화 할 필요가 없습니다.

프로파일 링을 통해 재 할당 횟수를 줄여야하고 메모리가 제한되지 않는 경우 각 방향으로 백분율을 초과 할당하고 원점에 오프셋을 저장하는 것이 좋습니다. (예를 들어, 1x1에서 새 비트 맵을 시작하고 9x9 배열을 보유하도록 할당하면 초기 값 xy오프셋이됩니다 4.) 여기서 오프셋은 픽셀 액세스 중에 추가 연산을 수행하여 오프셋을 적용해야합니다.

확장 프로그램이 정말 비싸다면 다음 중 하나 또는 둘 다를 시도해보십시오.

  • 세로 및 가로 확장을 다르게 처리하십시오. 새로운 블록을 할당하고 기존의 전체 어레이의 단일 사본을 새로운 메모리의 적절한 오프셋으로 수행함으로써 어레이를 어느 방향 으로든 수직으로 확장 할 수 있습니다. 기존 블록이 새 블록에서 연속적이지 않기 때문에 행당 한 번만 해당 작업을 수행해야하는 수평 확장과 비교하십시오.

  • 가장 빈번한 확장 방향과 방향을 추적하십시오. 이 정보를 사용하여 새 크기와 오프셋을 선택하면 확장에 대해 재 할당 및 복사가 필요할 가능성이 줄어 듭니다.

개인적으로, 픽셀 액세스 비율 확장이 낮지 않으면 둘 중 하나가 필요할 것입니다.


1
  • 일정한 크기의 타일링 (예 : 256x256, 무제한 타일 수)
  • 음의 픽셀 좌표를 허용 하는 비트 맵 래퍼 를 제공합니다 (기존 좌표 값에 대한 참조를 재 계산 / 동기화하지 않고도 이미지를 네 방향으로 모두 확장 할 수 있음).
    • 그러나 래퍼 아래에서 작동하는 실제 비트 맵 클래스는 절대 (음이 아닌) 좌표 만 지원해야합니다.
  • 랩퍼에서 메모리 맵핑 된 I / O를 사용하여 타일 레벨 (블록 레벨) 액세스를 제공하십시오.
  • 한 번에 단일 픽셀을 추가 Map.setPixel()하고 Map.getPixel()수정하는 것 외에도 한 번에 한 픽셀의 사각형 을 복사하고 수정하는 방법을 제공하십시오 . 이를 통해 발신자는 발신자에게 제공되는 정보에 따라보다 효율적인 액세스 방식을 선택할 수 있습니다.
    • 상업용 라이브러리는 한 행의 픽셀, 한 열의 픽셀, 산란 / 수집 업데이트 및 산술 / 논리적 블 리터 작업을 한 단계로 업데이트하는 방법도 제공합니다 (데이터 복사 최소화).

(재미있는 대답을 찬성하지 말자 ...)


0

가장 유연하고 안정적인 구현은 x 좌표, y 좌표 및 비트 값을 제공하는 구조체가 포함 된 연결 목록입니다. 나는 그것을 먼저 구축하고 작동시킬 것입니다.

그런 다음 너무 느리거나 큰 경우 일반적인 배열 속도, 배열, 행렬, 비트 맵, 압축, 캐싱, 반전, '1'값만 저장 등을 시도하십시오.

버그가있는 빠른 구현을 수정하는 것보다 느린 올바른 구현을 더 빨리 만드는 것이 더 쉽습니다. 그리고 '빠른'두 번째 구현을 테스트하는 동안 비교할 참조 표준이 있습니다.

그리고 아는 사람은 느린 버전이 충분히 빠르다는 것을 알게 될 것입니다. 전체 구조가 메모리에 들어가는 한, 이미 매우 빠릅니다.


3
-1 : "빠르게 만들기 전에 작동"은 최악의 구현으로 시작하는 좋은 이유가 아닙니다. 또한 기본 데이터 구조로 완전히 변경할 필요가없는 코드는 실제로 없으므로 해당 수준에서의 반복은 완전히 암시적인 제안입니다.
Michael Borgwardt

그렇기 때문에 API가 있습니다. API는 기본 구현을 숨 깁니다. SetValue (MyMatrix, X, Y, Value) 및 GetValue (MyMatrix, X, Y)는 MyMatrix가 1 또는 2 차원 배열인지, 또는 링크 된 목록인지, 디스크 나 SQL 테이블에 캐시되는지, 아니면 무엇이든 숨 깁니다. 호출자는 다시 컴파일해야하지만 코드를 변경하지 않아도됩니다.
Andy Canfield

0

파이썬에서 다음 구현을 제안합니다.

class Map(dict): pass

다음과 같은 장점이 있습니다.

  1. 를 통한 가져 오기 / 설정 액세스를 map[(1,2)]고려할 수 있습니다 O(1).
  2. 그리드를 명시 적으로 확장 할 필요가 없어집니다.
  3. 버그를위한 공간이 거의 없습니다.
  4. 필요한 경우 3D로 쉽게 업그레이드 할 수 있습니다.

0

이 경우 실제로 임의의 크기의 비트 맵을 필요로 - 그리고 1000000x1000000에 1 ×에서 나는 평균 아무것도가 AMD, 그것은 필요에 따라 확장 가능해야합니다 ... 하나의 가능한 방법은 데이터베이스를 사용하는 것입니다. 처음에는 직관적이지 않은 것처럼 보이지만 실제로 가지고있는 것은 스토리지 문제입니다. 데이터베이스를 사용하면 개별 픽셀에 액세스하고 본질적으로 모든 양의 데이터를 저장할 수 있습니다. 나는 반드시 SQL DB, btw를 의미하지는 않습니다.

당신의 목적을 위해 충분히 빠를까요? 이 비트 맵으로 무엇을하고 있는지에 대한 문맥이 없기 때문에 대답 할 수 없습니다. 그러나 이것이 화면 표시를위한 것이면 일반적으로 모든 데이터가 아니라 화면이 스크롤 될 때 표시하기 위해 추가 스캔 행만 풀어야한다는 것을 고려하십시오.

그건 그렇고, 당신이 뭔가 잘못되고 있는지 궁금해합니다. 대신 벡터 기반 그래픽을 사용하고 메모리에서 개별 요소를 추적 한 다음 화면에 필요한만큼만 비트 맵을 렌더링해야합니까?


아마도 OSGeo 맵 서버 일 것입니다.
rwong

0

제대로 작동하게하려면 다음 단계를 따르십시오.

  1. 한 인터페이스에서 다른 인터페이스로 전달을 사용하여 비트 맵을 함께 나타내는 객체 트리를 만듭니다.
  2. 비트 맵의 ​​모든 "확장"은 자체 메모리를 할당하고 다른 인터페이스를 제공합니다.
  3. 구현 예는 다음과 같습니다.

    template<class T, class P>
    class ExtendBottom {
    public:
       ExtendBottom(T &t, int count) : t(t), count(count),k(t.XSize(), count) { }
       P &At(int x, int y) const { if (y<t.YSize()) return t.At(x,y); else return k.At(x, y-t.YSize()); }
       int XSize() const { return t.XSize(); }
       int YSize() const { return t.YSize()+count; }
    private:
       T &t;
       int count;
       MemoryBitmap k;
    };
    

실제 구현에서는 분명히 XSize () 및 YSize ()가 작동하지 않지만 인덱스 번호의 일관성을 유지하려면 MinX (), MaxX (), MinY (), MaxY () 또는 이와 유사한 것이 필요합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.