2013-12-23 2 views
4

카메라에서 8 비트 회색 이미지를 수집하고 데이터를 WriteableBitmap에 배치하고 이미지를 WPF 이미지에 표시 할 수있는 시스템이 있습니다. 목적. 이 작업은 카메라 스레드에서 발생합니다. 이 기사를 통해 나를 도왔습니다. How to create a BitmapImage from a pixel byte array (live video display)사용자가 만든 픽셀 바이트 배열이 올바르게 업데이트되지 않는 경우 (WPF)

이미지 데이터의 픽셀 데이터 하위 집합의 복사본을 만드는 것입니다. 카메라 스레드에서 프레임 업데이트하는 동안 별도의 바이트 배열로 데이터의 복사본을 만들려고합니다. 내 코드는 처음에는 작동하지만 몇 번의 반복을 거친 후 버퍼 변수는 회색 레벨 범위 (0-255)에서 모든 배열 요소에 255의 값만 가지게됩니다. 이 변수는 백그라운드 작업자가 호출 될 때마다 재설정되는 것과 달리 데이터를 누적하는 것처럼 보입니다. 나는 아래에 나의 코드를 제시 할 것이다.

누구나 내가 뭘 잘못하고 있는지 설명 할 수 있습니까? 고맙습니다.

public partial class MainWindow : Window 
{ 
    [DllImport("Kernel32.dll",EntryPoint="RtlMoveMemory")] 
    public static extern void CopyMemory(IntPtr Destination, IntPtr Source, 
     uint Length); 

    // Declarations 

    var pData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte))*FrameSize); 
    var pFocusData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) 
      *FrameSize); 

    BackgroundWorker bw = new BackgroundWorker(); 
    static CameraWorker cWorker; 
    static Thread cThread; 

    WriteableBitmap wbm; 
    public IntPtr wbmBackBuffer; 
    const int FrameSize = 1944*2592; 

    // CameraWorker Event Handler 
    void CW_FrameUpdated(object sender, CameraWorkerEventArgs e) 
    { 
     if (!e.Updated) return; 
     // e.pData is an IntPtr containing the camera frame data 
     CopyMemory(this.wbmBackBuffer, e.pData, FrameSize); 
     this.Dispatcher.Invoke(wbm.Lock); 
     this.Dispatcher.Invoke(()=>{ wbm.AddDirtyRect(
      new Int32Rect(0,0,wbm.PixelWidth,wbm.PixelHeight)); }); 
     this.Dispatcher.Invoke(wbm.Unlock); 

     // The above works and I get streaming data to my view port. 
     // Now I want to make a copy of the pixel data to send to another thread 
     // for processing. This is where I am having trouble. 
     if (bw.IsBusy) return; 
     CopyMemory(pFocusData, e.pData, FrameSize); 
     var args = new List<object>(); 
     args.Add(pFocusData); 
     bw.RunWorkerAsync(args); 
    } 

    // BackgroundWorker event handlers 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 

     // This is where I see the result of the problem when debugging. 

     List<object> argu = e.Argument as List<object>; 
     var pData = (IntPtr) argu[0]; 
     var fullFrame = new byte[FrameSize]; 

     Marshal.Copy(pData,fullFrame,0,FrameSize); 

     // Perform operations on the byte array data. 

     // I extract a subregion from the byte array to process, however after a 
     // couple of iterations, all values in fullFrame equal 255. The pData that 
     // is coming in should be a copy of the pixel data that is being displayed 
     // on the screen. While the screen keeps updating with a live video image, 
     // the frameData variable appears to keep accumulating rather than resetting 
     // with each backgroundworker call. 

    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 

     // Update UI elements using Dispatcher with data obtained during DoWork. 

    } 

    // Window event handlers 

    private void MainWindow_Initialized(object sender, EventArgs e) 
    { 
     // Set up the WBM 
     wbm = new WriteableBitmap(width,height,96d,96d,PixelFormats.Gray8,null); 
     this.wbmBackBuffer = wbm.BackBuffer; 

     // Set up the camera to grab frames in another thread 
     cWorker = new CameraWorker(camera); 
     cWorker.CameraFrameUpdated += CW_FrameUpdated; 
     cThread = new Thread(new ThreadStart(cWorker.ThreadRun)); 
     cThread.Start(); 
     while(!cThread.IsAlive); 

     // Set up the background worker for processing image data 
     bw.DoWork += bw_DoWork; 
     bw.RunWorkerCompleted += bw_RunWorkerCompleted; 

     // Bind the image data to the Image object that has the name "viewer" 
     viewer.Source = wbm; 
    } 

    private void MainWindow_Closing(object sender, 
            System.ComponentModel.CancelEventArgs e) 
    { 
     Marshal.FreeHGlobal(pData); 
    } 

} 

편집 : Erti-Chris Eelmaa가 지적한 오타를 수정했습니다. 내 코드의 관련 부분을 보여주는 데있어서 필사적으로 오류가있었습니다.

편집 # 2 :

1) (e.Updated) 반환하는 경우 한 후 BW 물건을하면 어떻게됩니까;! 선? wbm이 누적 오류가 발생하여 BW가 정상적으로 작동합니까?

동작에 차이가 없습니다. wbm은 여전히 ​​양호하며 BW 변수가 누적됩니다.

2) BackgroundWorker.IsBusy 속성은 bw_DoWork가 완료된 후 false가됩니다. 이 행동에 괜찮습니까?

내 의도는 BW가 완료되었을 때만 새 프레임을 처리하는 것이 었습니다. 나는 IsBusy가 RunWorkerCompleted가 실행될 때까지 지속될 것이라고 생각했다. 모든 프레임을 처리 할 필요가 없습니다.

CW_FrameUpdated를 호출 할 때마다 새 BW를 간단하게 만들려고했으나 문제가 해결되지 않았습니다.

3) MoveMemory의 기능은 무엇입니까? SOURCE에서 아무 것도 변경하지 않고 SOURCE에서 DESTINATION으로 메모리를 복사합니까? 아무것도 복사하지 않니?

RtlMoveMemory (CopyMemory)는 메모리의 한 영역에서 다른 영역으로 바이트를 복사한다고 가정합니다. AllocHGlobal 함수에 의해 동일한 크기의 두 개의 공간을 할당함으로써 한 변수에서 다른 변수로 8MB의 데이터를 신속하게 복사 할 수 있다고 생각했습니다. 내가 읽은 것에서는 내가 관리하는 기억이 좋아하지 않는 일을하고있는 것처럼 보입니다. 데이터의 빠른 전체 사본이 필요합니다. 내가 처음 뭔가를 놓친 경우를 대비하여 System.Buffer.BlockCopy를 다시 시도 할 것입니다.

4) 어떤 버퍼 변수에 대해 말하고 있습니까? 모든 단계에서 모든 버퍼를 확인하고 버퍼가 다른 정확한 위치를 정하십시오. DumpMemory (IntPtr unmanagedMemory) 함수를 만들어야합니다. IntPtr을 바이트으로 캐스팅하고 fixed() 문을 사용하여 수행 할 수 있습니다. *

DumpMemory 함수를 설정하고 내가 찾은 것을 알려줍니다. 그 동안 데이터 흐름과 관련 변수 목록이 있습니다.

pData의 (을 IntPtr), 헤더와 Gray8 형식의 화상 데이터를 1944 * 2592 바이트에 포함되지 않는 메모리

WBM (WriteableBitmap) 메인 화면의 이미지 오브젝트에 바인딩 WPF 변수

wbmBackBuffer (을 IntPtr)에서 CopyMemory를 사용 wbmBackBuffer 복사 될 wbm.BackBuffer

pData의 같은 위치에 포인트, WBM가 화상 객체로 연결되어 있기 때문에, 현재의 영상 프레임이 메인 갱신되는 로컬 변수 창

pFocusData (IntPtr) : 데이터의 전체 프레임 크기로 메모리에 할당 된 로컬 포인터입니다.

pData는 CopyMemory를 통해 pFocusData에 복사됩니다.

fullFrame (byte []) : 이것은 pFocusData의 바이트 배열 복사본입니다. 이것은 축적이 일어나는 곳입니다.

편집 # 3 : 마침내 문제가 해결되었습니다. 내가 선택한 하위 어레이에 논리 오류가있는 것으로 나타났습니다. 내보기 포트는 약 800 * 400이고 이미지 배열은 약 3.5 배 더 큽니다. 좌표를 적절하게 스케일하지 않았기 때문에 샘플 영역은 모두 프레임 데이터의 작고 비슷한 영역을보고있었습니다. 나는 네 번째 제안을 따르고 메모리 덤프를 사용하여 각 단계에서 정확히 무슨 일이 벌어 졌는지 확인함으로써이를 알 수있었습니다. 결과적으로 코드에 아무런 문제가 없다는 것을 알게되었고 축적 된 것이라고 생각한 것은 사실 작은 영역에 카메라가 포화 된 것입니다.

좋은 소식은 위에 게시 된 코드가 정확하고 작동한다는 것입니다. 결국 궁극적으로 Buffer.BlockCopy를 사용하여 끝내고 카메라 작업자 이벤트 args를 통과하는 복제 프레임을 만들었습니다.

감사합니다. Erti-Chris Eelmaa! 당신이 피곤하고

답변

1

, 그것은 지금 크리스마스 시간 : P // 편집

if (bw.IsBusy) return; 
    // replace this.wbmBackBuffer with pFocusData 
    CopyMemory(this.wbmBackBuffer, e.pData, FrameSize); 
    var args = new List<object>(); 
    args.Add(pFocusData); 
    bw.RunWorkerAsync(args); 

, 그래서 당신의 코드를보고, 나는 몇 가지 질문/제안이있다.

1) if (!e.Updated) return; 라인 이후에 BW 물건을 만들면 어떻게됩니까? wbm이 누적 오류가 발생하여 BW가 정상적으로 작동합니까?

2) BackgroundWorker.IsBusy 속성은 bw_DoWork가 완료된 후에 false가됩니다. 이 행동에 괜찮습니까?

3) MoveMemory의 기능은 무엇입니까? SOURCE에서 아무 것도 변경하지 않고 SOURCE에서 DESTINATION으로 메모리를 복사합니까? 아무것도 복사하지 않니?

4) 어떤 버퍼 변수에 대해 말하고 있습니까? 모든 단계에서 모든 버퍼를 확인하고 버퍼가 다른 정확한 위치를 정하십시오. DumpMemory (IntPtr unmanagedMemory) 함수를 만들어야합니다. IntPtr을 byte *로 형변환하고 fixed() 문을 사용하여 그렇게 할 수 있습니다.

+0

피곤하지만 연말까지이 코드를 마무리해야합니다. 내가 찾은 오타는 관련 코드를 복사/붙여 넣기하여 표시 한 것입니다. 문제가 여전히 존재합니다. Buffer.BlockCopy 데이터를 시도했지만 값을 덮어 쓰지 않고 복사 한 데이터가 누적되는 것과 동일한 문제가 발생합니다. 나는 다음으로 빠른 복제에 대해 살펴볼 것입니다. 데이터는 8 메가 픽셀이고 복사 픽셀은 너무 느립니다.필자는 전체 프레임 (10 개의 2 차원 영역에서 1k)에서 약 10k 픽셀 만 필요하지만 데이터가 관리되지 않는 메모리에있는 동안 데이터를 추출하는 방법을 잘 모르겠습니다. – CDitchman

+0

조금 업데이트되었습니다. 문제를 정확하게 해결 한 다음 성능 튜닝을 살펴 보겠습니다. –

+0

질문에 대한 응답으로 질문을 업데이트했습니다. 나는 며칠 동안 여행 중이었고 더 일찍 답할 수 없었습니다. 내가 제안한대로 메모리 덤프를 사용하여 디버깅 작업을하고 있습니다. – CDitchman

관련 문제