none
撰寫CCD取像時所發生的問題 RRS feed

  • 問題

  • 各位好,我目前在開發CCD攝影機的程式,

    是利用網卡接HUB再接兩隻CCD攝影機,主要希望程式上有兩個畫面同時顯示動態影像

    目前廠商寫了一個範例程式供我開發練習,是用兩個panel繪圖上去,

    但我才執行時,就會兩個畫面剛顯示影像後不久,就會再CCD相機的CALLBACK的地方跳出""System.AccessViolationException: 嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。   於 SVS2GigaCamCs.frmMain.copyBW828(Byte* source, Byte* dest, Int32 bytesize)   於 SVS2GigaCamCs.frmMain.myStreamCallback1(Int32 CamImage, Int32 Context) 於 D:\\切割機\\C#\\雙CCD\\NoStop\\SVS2GigaCamCs\\SVS2GigaCamCs\\SVS2GigaCamCs \\frmMain.cs: 行 232""的問題

    但這範例在廠商那邊跑是不會有問題,而我換了兩台電腦都一樣

    我就將那段程式多加了TRY和CATCH來做例外,雖可以將上述的問題一直跳過

    但再過不久就會有錯誤畫面:"偵測到 CallbackOnCollectedDelegate
    Message: 已在型別 'SVS2GigaCamCs!SVSGigeApi.GigeApi+StreamCallback::Invoke' 的記憶體回收委派上進行回呼。這樣可能導致應用程式無法運作、損毀或遺失資料。傳遞委派到 Unmanaged 程式碼時,必須由 Managed 應用程式讓它們保持運作,直到確定不會再呼叫它們為止。
    "

    這也是廠商不會發生的問題,我與廠商目前還在找問題,在此也想聽聽各位的意見,以下附上程式碼

    請各位參考看看,謝謝。

    開發環境 vs2005.net,xp SP3,

    CPU Pentium(R) Dual-Core E6300 @2.8GHz

    Ram 2.00GB

    NVIDIA GeForce 9400GT

    HUB和網卡都是GIGE 1000MB的規格

     

     

        [DllImport("ImageProcDll.Dll")]
        private static extern unsafe void copyBW828(byte* source, byte* dest, int bytesize);
    
        [return: MarshalAs(UnmanagedType.Error)]
        public GigeApi.SVSGigeApiReturn myStreamCallback1(int CamImage, int Context)
        {
          int xSize, ySize, size;
          IntPtr imgPtr1, imgPtr2;   
          try
          {
            unsafe
            {
              int iNumCam;
              iNumCam = myApi.Gige_Image_getCamera(CamImage);
              try
              {
                if (myApi.Gige_Image_getCamera(CamImage) == hCamera[0])
                {
                  imgPtr1 = myApi.Gige_Image_getDataPointer(CamImage);
                  xSize = myApi.Gige_Image_getSizeX(CamImage);
    
                  fixed (byte* dest1 = imagebytes1)
                  {
                    copyBW828((byte*)imgPtr1, dest1, camWidth1 * camHeight1);//--->錯在這行,會有記憶體毀損的錯誤訊息
                    DispRaw(0);
                    iCCD1Count++;
                  }
                }
                else if (myApi.Gige_Image_getCamera(CamImage) == hCamera[1])
                {
                  imgPtr2 = myApi.Gige_Image_getDataPointer(CamImage);
                  xSize = myApi.Gige_Image_getSizeX(CamImage);
                  fixed (byte* dest2 = imagebytes2)
                  {
                    copyBW828((byte*)imgPtr2, dest2, camWidth2 * camHeight2);
                    DispRaw(1);
                    iCCD2Count++;
    
                  }
    
                }
    
              }
              catch (Exception ex)
              {
                string er = ex.ToString();
                 MessageBox.Show(ex.ToString());
              }
    
            }
          }
          catch
          {
     
          }    
          return (GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS);
        }
    
    
     private static unsafe void copyBW8To8bit(byte[] outp, byte[] inp, int size)
        {
          fixed (byte* source = inp)
          fixed (byte* dest = outp)
          {
            copyBW828(source, dest, size);
          }
        }
    
    
    void DispRaw(int CamNum)
        {
          Point LP = new Point(0,0);
         
          if (CamNum == 0)
          {
             gpanel1.DrawImage(bimage1,LP);
          }
          else
          {
             gpanel2.DrawImage(bimage2,LP);
          }
    
        }
    
    
     private void btnGrab2_Click(object sender, EventArgs e)
        {
          this.initializeBuffer();
          this.startGrabbing();
        }
    
     //---------------------------------------------------------------------------
        // initialize and allocate memory buffers
        //---------------------------------------------------------------------------
        private void initializeBuffer()
        {  
          
          int k;
          imagebuffer1 = new imagebufferStruct[bufferCount];
          outputbuffer1 = new imagebufferStruct[bufferCount];
    
            imagebytes1 = new byte[camHeight1 * camWidth1];
            imagebytes2 = new byte[camHeight2 * camWidth2];
            
            unsafe
            {        
              {            
                fixed (byte* MonoPtr1 = imagebytes1)
                {
                  bimage1 = new Bitmap(camWidth1, camHeight1, camWidth1, PixelFormat.Format8bppIndexed, (IntPtr)MonoPtr1);
                }
    
                fixed (byte* MonoPtr2 = imagebytes2)
                {
    
                  bimage2 = new Bitmap(camWidth2, camHeight2, camWidth2, PixelFormat.Format8bppIndexed, (IntPtr)MonoPtr2);
            
                }                    
                imgpal = bimage1.Palette;
                imgpal2 = bimage1.Palette;
    
                // Build bitmap palette Y8
                for (uint i = 0; i < 256; i++)
                {
                  imgpal.Entries[i] = Color.FromArgb(
                  (byte)0xFF,
                  (byte)i,
                  (byte)i,
                  (byte)i);
                  imgpal2.Entries[i] = Color.FromArgb(
          (byte)0xFF,
          (byte)i,
          (byte)i,
          (byte)i);
                }
                bimage1.Palette = imgpal;
                bimage2.Palette = imgpal2;
    
                // imgpal = bimage[k].Palette;
              }
            }
    
        }
    
     //---------------------------------------------------------------------------
        // TCP/IP connection has already been established to a selected camera
        //---------------------------------------------------------------------------
        private void startGrabbing()
        {
    
    
          if (initgrab == false)
          {
    
            initgrab = true;
    
            if (GigeIsInit == true) // = true, if connection to the camera is established and the panel and Buffer 
            //      are initialized
            {
    
              notFirstImage = false;
    
              unsafe
              {
    
                // set freerunning - mode
                errorflag = myApi.Gige_Camera_setAcquisitionMode(hCamera[0], GigeApi.ACQUISITION_MODE.ACQUISITION_MODE_FIXED_FREQUENCY);//GigeApi.ACQUISITION_MODE.ACQUISITION_MODE_FIXED_FREQUENCY
                errorflag = myApi.Gige_Camera_setAcquisitionMode(hCamera[1], GigeApi.ACQUISITION_MODE.ACQUISITION_MODE_FIXED_FREQUENCY);//GigeApi.ACQUISITION_MODE.ACQUISITION_MODE_FIXED_FREQUENCY
    
                if (errorflag != GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS)
                {
                  MessageBox.Show("Errorflag: " + errorflag.ToString());
    
                }
    
              }
      
              grab = true;
    
    
            } // if GigeIsInit
    
            initgrab = false;
    
          } //if initgrab
    
        }
    以上為部份程式碼,如有不清楚的地方我再補,謝謝

    2010年7月6日 上午 03:48

解答

  • 感謝諸位的回覆,目前我得到廠商新的回覆,

    就雙CCD的部份 廠商設計了新的寫法,目前測試是還正常,

    但有兩個小問題想提出來請大家指教一下,以下是原廠新的部份程式碼:

    public struct CameraInfo
    {
      public GigeApi Camera;
      public GigeApi.StreamCallback OnImage;
      public System.Drawing.Rectangle outRectangle;
      public Graphics gpanel;
    };
    delegate void SetStatusBarCallBack();
    private CameraInfo[] Camera;
    
    
    //按鈕:連接兩支CCD
    private void btn_CCD_Discover_Click(object sender, EventArgs e)
    {
      //第一支CCD
      Camera[0].gpanel = panel1.CreateGraphics();
      Camera[0].Camera = new GigeApi();
      Camera[0].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[0].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
      Camera[0].Camera.RegisterImageCallback(Camera[0].OnImage);
      Camera[0].Camera.Initialize(SN_Camera0, 3, 0);
    
      //第二支CCD
      Camera[1].gpanel = panel2.CreateGraphics();
      Camera[1].Camera = new GigeApi();
      Camera[1].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[1].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
      Camera[1].Camera.RegisterImageCallback(Camera[1].OnImage);
      Camera[1].Camera.Initialize(SN_Camera0, 3, 0);
    }
    
    
    //繪至影像的方法
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
      GigeApi.tcImageInfo info = new GigeApi.tcImageInfo();
      
      while (true)
      {
        bool ret = Camera[Context].Camera.GetImage(ref info);
    
        if (ret)
        {
          lock (info.bitmap)
          {
            lock (Camera[Context].gpanel)
            {
              Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);              
            }
          }
    
        }
        else
        {
          break;
        }
      }
      return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }
    

     

    第一個問題:

    上面原廠程式中,有寫好一個方法叫OnImage,是不停地取像繪圖到FORM 上的panel上,應該就是programlin先生 說的非同步呼叫.

    現在我希望在這方法中,增加我想要作的影像運算,

    比如說設一個全域的bool變數TakeImage,當變數為true時,就在OnImage方法中複製出一張目前畫面的Bitmap格式影像到一個全域變數 Bmp_1中,

    然後對那張Bmp_1作影像運算。

    目前我已可在特定時機複製出當下畫面到Bmp_1變數中,請看以下程式碼:

    //對原本的方法多加複製影像到Bmp_1
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
      GigeApi.tcImageInfo info = new GigeApi.tcImageInfo();
      
      while (true)
      {
        bool ret = Camera[Context].Camera.GetImage(ref info);
    
        if (ret)
        {
          lock (info.bitmap)
          {
            lock (Camera[Context].gpanel)
            {
              Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);
    
              //--------------------------------------------------------------------------------------------//
              //這裡是我所複製出一個8bit影像 Bmp_1,之後要對Bmp_1用來運算出值
              if (TakeImage == true)//當目前畫面是我想取的影像時,複製他到 Bmp_1
              {
                unsafe
                {
                  fixed (byte* Ptr = info.ImageData)
                  {
                    Bmp_1 = new Bitmap(640, 480, 640, PixelFormat.Format8bppIndexed, (IntPtr)Ptr);
                    imgpal = Bmp_1.Palette;
                    // Build bitmap palette Y8
                    for (uint i = 0; i < 256; i++)
                    {
                      imgpal.Entries[i] = Color.FromArgb(
                      (byte)0xFF,
                      (byte)i,
                      (byte)i,
                      (byte)i);
                    }
                    Bmp_1.Palette = imgpal;
                  }
                }
              }
              //----------------------------------------------------------------------------------------------//
            }
          }
    
        }
        else
        {
          break;
        }
      }
      return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }
    
    

    且我寫了一個影像處裡方法imageprocess(),打算要同時呼叫這影像處裡方法來運算出一些數值,

    而我的問題是,

    這個imageprocess()方法的運算量有一點大,但我希望能在短暫的瞬間下就回傳出值,(因為我會取很多張畫面來運算,數值的順序不能錯)

    這樣的情況下有人是建議我用thread來做,

    因此我想請教的部份就是thread寫的動作和位置,

    new的時機、start的位置等等,這些想請各位實際指導一下,

    我有在lock中,也就是上面unsafe括號後試寫這樣:

    Thread imgproc = new Thread(new ThreadStart(this.imageProcessing));
    imgproc.Start();
    TakeImage = false;//複製取像關閉

    結果算出得到數據要非常久,不但CCD的取像畫面會停掉,還只有取像第一次能算出值,之後因CCD畫面停掉就都傳不進OnImage中,

    我改在lock外寫也是一樣

    我相信是我thread這部份寫法有錯,

    想請各位指導一下正確寫法,能即時處理的方式,在此感謝。

     

    第二個問題是重繪的問題,當form上有多開其他的form視窗時,

    會造成顯示CCD動態畫面的panel會消失畫面,或是跳其他視窗時有時也會消失,

    變成背景panel顏色,但CCD還是有在連結的狀態,

    我是有寫:

    private void Form_Main_Paint(object sender, PaintEventArgs e)
    {
                panel1.Refresh();
    }

    情況雖好了一些,但還是不一定,這點想請教是否有要改進的地方

     

    以上兩個問題,還請諸位再指導寫法,謝謝!!

     

     

     

     

    • 已標示為解答 Lolota Lee 2010年7月13日 上午 02:06
    2010年7月11日 上午 04:02

所有回覆

  • CopyBW828在原廠手冊上是怎麼說明這個Method的 ?

    以下是簽名檔, 請勿沒事對號入座
    MSDN 文件庫很重要
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    進步的人會找尋自己程式中的缺點,半桶水則把自己程式的錯誤推到不相干事物的身上
    2010年7月6日 上午 04:08
    版主
  • 看你的程式myStreamCallback1這函式看起來應該是非同步呼叫.
    如果是如此我猜測你的程式的問題應該是multithread常發生的資源同時存取的thread saft問題,很明顯的你的程式相關函式都未保證thread saft所以最簡單的方式就是在myStreamCallback1一開始做lock機制,先確認是否是因為我所說的問題.如果lock後可正常運作(不考慮效能),再來慢慢從程式架構處理非同步問題.

    2010年7月6日 上午 05:27
  • 非常謝謝兩位的回覆

    目前copyBW828這方法在說明文件是沒有記載
    但問了代理商是得知,是以指標的方式複製一個8BIT的影像,

    programlin 先生提到可能原因是multithread常發生的資源同時存取的thread saft問題,
    那想請教一下這lock的用法,
    因為我是希望設計為同時兩個CCD攝影機擷取畫面,分別繪製在兩個畫面上
    因此我在 public GigeApi.SVSGigeApiReturn myStreamCallback1(int CamImage, int Context) 中

    會用下述的方式區分指到的畫面是哪一隻CCD的畫面
    if (myApi.Gige_Image_getCamera(CamImage) == hCamera[0])
    {.... }                      
    else if (myApi.Gige_Image_getCamera(CamImage) == hCamera[1])
    {....}

    請問這樣會影響lock的寫法嗎?
    會要分別寫2個lock對這個判斷式嗎?
    還是說是一個lock直接包在最外層(unsafe外面)嗎?

    關於lock的知識不是很清楚,只知道用fixed來試試看,且我沒見過2個lock以上的寫法,
    想問問看需不需要這樣寫,還是說根本不是這樣的,

    還請前輩再解惑一下

    謝謝

    2010年7月6日 上午 06:38
  • 嘗試看看在程式中加入底下程式碼做測試

    1.
    新增一個member於class中
    class XXX
    {
    private object lockObject = new object();

    2.在你的程式呼叫copyBW828之前後加上lock敘述
    lock(lockObject )
    {
    copyBW828(xxx);
    }

    測試看看是否會發生錯誤.

    此外上面呼叫須確認呼叫myStreamCallback1的呼叫端是同一個instance,如不能確定把private object lockObject = new object();修改為private static object lockObject = new object();

     

     

    2010年7月6日 上午 06:51
  • programlin 先生您好

    我在CLASS內加了 private static object lockObject = new object();後

    試改了兩種方式,結果一樣會出現一樣的上述兩個錯誤,

    "System.AccessViolationException: 嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。"

    例外掠過後變成"偵測到 CallbackOnCollectedDelegate
    Message: 已在型別 'SVS2GigaCamCs!SVSGigeApi.GigeApi+StreamCallback::Invoke' 的記憶體回收委派上進行回呼。這樣可能導致應用程式無法運作、損毀或遺失資料。傳遞委派到 Unmanaged 程式碼時,必須由 Managed 應用程式讓它們保持運作,直到確定不會再呼叫它們為止。"

     

    我改的程式碼如下

    第一種方式,用一個LOCK寫在unsafe外

     [return: MarshalAs(UnmanagedType.Error)]
        public GigeApi.SVSGigeApiReturn myStreamCallback1(int CamImage, int Context)
        {
         
          int xSize, ySize, size;
          IntPtr imgPtr1, imgPtr2;   
          try
          {
            lock (lockObject)
            {
              unsafe
              {
                int iNumCam;
                iNumCam = myApi.Gige_Image_getCamera(CamImage);
    
                try
                {
    
                  if (myApi.Gige_Image_getCamera(CamImage) == hCamera[0])
                  {
                    //copyBW828(source, dest, size);
                    //byte* src = (byte*)imgPtr;
                    imgPtr1 = myApi.Gige_Image_getDataPointer(CamImage);
                    xSize = myApi.Gige_Image_getSizeX(CamImage);
                    fixed (byte* dest1 = imagebytes1)
                    {
    
                      copyBW828((byte*)imgPtr1, dest1, camWidth1 * camHeight1);
                      DispRaw(0);
                      iCCD1Count++;
    
                    }
    
    
                  }
                  else if (myApi.Gige_Image_getCamera(CamImage) == hCamera[1])
                  {
                    imgPtr2 = myApi.Gige_Image_getDataPointer(CamImage);
                    xSize = myApi.Gige_Image_getSizeX(CamImage);
                    fixed (byte* dest2 = imagebytes2)
                    {
                      copyBW828((byte*)imgPtr2, dest2, camWidth2 * camHeight2);
                      DispRaw(1);
                      iCCD2Count++;
    
                    }
    
                  }
    
                }
                catch (Exception ex)
                {
                  //string er = ex.ToString();
                  //MessageBox.Show(ex.ToString());
                }
    
              }
            }
            
          }
          catch
          {
     
          }    
          return (GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS);
        }
    

    第二種方式,寫兩個lock在兩個copyBW828

     

     [DllImport("ImageProcDll.Dll")]
        private static extern unsafe void copyBW828(byte* source, byte* dest, int bytesize);
    
        [return: MarshalAs(UnmanagedType.Error)]
        public GigeApi.SVSGigeApiReturn myStreamCallback1(int CamImage, int Context)
        {     
          int xSize, ySize, size;
          IntPtr imgPtr1, imgPtr2;   
          try
          {       
              unsafe
              {
                int iNumCam;
                iNumCam = myApi.Gige_Image_getCamera(CamImage);
                try
                {
                  if (myApi.Gige_Image_getCamera(CamImage) == hCamera[0])
                  {
                    //copyBW828(source, dest, size);
                    //byte* src = (byte*)imgPtr;
                    imgPtr1 = myApi.Gige_Image_getDataPointer(CamImage);
                    xSize = myApi.Gige_Image_getSizeX(CamImage);
                    fixed (byte* dest1 = imagebytes1)
                    {
                      lock (lockObject)
                      {
                        copyBW828((byte*)imgPtr1, dest1, camWidth1 * camHeight1);
                      }
                      DispRaw(0);
                      iCCD1Count++;
    
                    }
    
                  }
                  else if (myApi.Gige_Image_getCamera(CamImage) == hCamera[1])
                  {
                    imgPtr2 = myApi.Gige_Image_getDataPointer(CamImage);
                    xSize = myApi.Gige_Image_getSizeX(CamImage);
                    fixed (byte* dest2 = imagebytes2)
                    {
                      lock (lockObject)
                      {
                        copyBW828((byte*)imgPtr2, dest2, camWidth2 * camHeight2);
                      }
                      DispRaw(1);
                      iCCD2Count++;
    
                    }
    
                  }
    
                }
                catch (Exception ex)
                {
                  //string er = ex.ToString();
                  //MessageBox.Show(ex.ToString());
                }
    
              }
            
            
          }
          catch
          {
     
          }    
          return (GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS);
        }

     

    以上是我所修改的部分

    不知有沒有寫法的問題,還請指導一下

    謝謝。

     

     

     

    2010年7月6日 上午 08:11

  • 目前copyBW828這方法在說明文件是沒有記載
    但問了代理商是得知,是以指標的方式複製一個8BIT的影像,

    文件沒有寫? 那你怎知道是這樣宣告?

     private static extern unsafe void copyBW828(byte* source, byte* dest, int bytesize);


    以下是簽名檔, 請勿沒事對號入座
    MSDN 文件庫很重要
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    進步的人會找尋自己程式中的缺點,半桶水則把自己程式的錯誤推到不相干事物的身上
    2010年7月6日 上午 08:43
    版主
  • 那可能非thread safe問題了,應該是copyBW828這win32 api本身的問題.

    2010年7月6日 上午 08:51
  • Bill Chung先生您好

    關於copyBW828宣告,也是看到原廠的範例程式裡面有才會宣告的, 不好意思。

    由於是在dll中,也無法切確知道內容是怎麼做的...

    真抱歉。

    2010年7月6日 上午 09:03
  • programlin 先生再請教您一下

    我以上修改的兩個lock寫法是正確的嗎?

    因為也不確定是該寫一個lock 包在外面,還是用兩個lock 個別包住,

    可以指示一下嗎?

     

    另外我是想,或許我可以自己複製那一塊記憶體,而不要用那個copyBW828的話,應該可以避免那未知的錯誤

    因此是想再請教一個小技巧,

    就是已經知道IntPtr imgPtr1;這一個指標是指向那一個影像,

    如果我想另外準備一塊記憶體,把imgPtr1指到的內容複製過去,

    繪圖用那一塊新的記憶體來繪,而將imgPtr1的內容釋放掉,

    請問這樣的想法是可行的嗎?

    如果可以,那複製這imgPtr1指標指的內容,語法是怎麼寫呢?

    而釋放掉imgPtr1指的內容的寫法又是如何來寫呢?

    一點小構想,請指教批評

    謝謝

     

     

     

     

    2010年7月6日 上午 09:17
  • Bill Chung先生您好

    關於copyBW828宣告,也是看到原廠的範例程式裡面有才會宣告的, 不好意思。

    由於是在dll中,也無法切確知道內容是怎麼做的...

    真抱歉。

    因為我很想弄清楚, 那再問一個問題.

    原廠的範例也是用C#寫的, 還是C++ ?

     


    以下是簽名檔, 請勿沒事對號入座
    MSDN 文件庫很重要
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    進步的人會找尋自己程式中的缺點,半桶水則把自己程式的錯誤推到不相干事物的身上
    2010年7月6日 上午 09:51
    版主
  • 上面的lock只是一個很簡單的動作,lock區間主要的目的是不會同時間程式會進入一次以上.
    因為你的程式中有用了相當多的指標與unsfae的C++程式.而這部分如果有同時間有有兩個以上程式存取相同資料就會發生問題,至於lock應該擺在何處,必須看你的程式哪邊會同時發生存取的狀況而定,當然lock的範圍越大效能就越差,而也越安全.上面的lock作法在效能上不是很很理想,multithread有很多處理資料同步的機制,lock是最簡單也最沒效率的方式.

    至於在.Net中使用C++指標問題,因為我目前工作已經很久不會碰觸這些底層的運作,所以沒有太多研究,但給個建議,在.Net中靠得是GC來做記憶體回收而GC的機制而不需要像C++作記憶體配置與釋放,所以除非有很大的效能上問題,建議你將非API部分的程式用C#撰寫會簡單且問題容易處理.當然你的程式由於是做影像處理可能在效能上必須這樣做,這部分必須視你的狀況而定.
    其實在C#中使用unsfae不是個好方式,最好的做法是將unsfae相關程式封裝在C++ dll中,如同你的copyBW828函數一樣供.Net用PInvoke方式呼叫,而C#作邏輯上之撰寫,這部分關連到架構該如何定義.簡單的說就是效能上取決的封裝在C++的win32 dll,而邏輯與UI則靠C#撰寫.

    2010年7月6日 上午 10:12
  • Bill Chung先生您好

    原廠的範例是用C#寫的,但同時間只能連結一隻CCD,

    因此我是請代理商協助寫一個可同時顯示兩支CCD的範例程式

    但目前是我使用這個代理商的範例程式時,

    會不固定時間會出現這樣的錯誤,

    有時是出現在切換其他的視窗(如"我的電腦",OUTLOOK等)就會出現錯誤

    而代理商的說法是沒有遇到,這點我一直在想設法解決..

     

    2010年7月6日 上午 10:48
  • 所以本來一隻CCD沒有問題, 然後變兩隻CCD就會出包 ? 依據以前的經驗, 這可能由於使用兩個CCD時在配置記憶體空間出問題 (ex: 互相覆蓋記憶體 ). 因為unsafe code不像safe code這樣可以讓CLR幫你處理記憶體配置.

    以下是簽名檔, 請勿沒事對號入座
    MSDN 文件庫很重要
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    進步的人會找尋自己程式中的缺點,半桶水則把自己程式的錯誤推到不相干事物的身上
    2010年7月6日 上午 11:09
    版主
  • 感謝諸位的回覆,目前我得到廠商新的回覆,

    就雙CCD的部份 廠商設計了新的寫法,目前測試是還正常,

    但有兩個小問題想提出來請大家指教一下,以下是原廠新的部份程式碼:

    public struct CameraInfo
    {
      public GigeApi Camera;
      public GigeApi.StreamCallback OnImage;
      public System.Drawing.Rectangle outRectangle;
      public Graphics gpanel;
    };
    delegate void SetStatusBarCallBack();
    private CameraInfo[] Camera;
    
    
    //按鈕:連接兩支CCD
    private void btn_CCD_Discover_Click(object sender, EventArgs e)
    {
      //第一支CCD
      Camera[0].gpanel = panel1.CreateGraphics();
      Camera[0].Camera = new GigeApi();
      Camera[0].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[0].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
      Camera[0].Camera.RegisterImageCallback(Camera[0].OnImage);
      Camera[0].Camera.Initialize(SN_Camera0, 3, 0);
    
      //第二支CCD
      Camera[1].gpanel = panel2.CreateGraphics();
      Camera[1].Camera = new GigeApi();
      Camera[1].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[1].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
      Camera[1].Camera.RegisterImageCallback(Camera[1].OnImage);
      Camera[1].Camera.Initialize(SN_Camera0, 3, 0);
    }
    
    
    //繪至影像的方法
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
      GigeApi.tcImageInfo info = new GigeApi.tcImageInfo();
      
      while (true)
      {
        bool ret = Camera[Context].Camera.GetImage(ref info);
    
        if (ret)
        {
          lock (info.bitmap)
          {
            lock (Camera[Context].gpanel)
            {
              Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);              
            }
          }
    
        }
        else
        {
          break;
        }
      }
      return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }
    

     

    第一個問題:

    上面原廠程式中,有寫好一個方法叫OnImage,是不停地取像繪圖到FORM 上的panel上,應該就是programlin先生 說的非同步呼叫.

    現在我希望在這方法中,增加我想要作的影像運算,

    比如說設一個全域的bool變數TakeImage,當變數為true時,就在OnImage方法中複製出一張目前畫面的Bitmap格式影像到一個全域變數 Bmp_1中,

    然後對那張Bmp_1作影像運算。

    目前我已可在特定時機複製出當下畫面到Bmp_1變數中,請看以下程式碼:

    //對原本的方法多加複製影像到Bmp_1
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
      GigeApi.tcImageInfo info = new GigeApi.tcImageInfo();
      
      while (true)
      {
        bool ret = Camera[Context].Camera.GetImage(ref info);
    
        if (ret)
        {
          lock (info.bitmap)
          {
            lock (Camera[Context].gpanel)
            {
              Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);
    
              //--------------------------------------------------------------------------------------------//
              //這裡是我所複製出一個8bit影像 Bmp_1,之後要對Bmp_1用來運算出值
              if (TakeImage == true)//當目前畫面是我想取的影像時,複製他到 Bmp_1
              {
                unsafe
                {
                  fixed (byte* Ptr = info.ImageData)
                  {
                    Bmp_1 = new Bitmap(640, 480, 640, PixelFormat.Format8bppIndexed, (IntPtr)Ptr);
                    imgpal = Bmp_1.Palette;
                    // Build bitmap palette Y8
                    for (uint i = 0; i < 256; i++)
                    {
                      imgpal.Entries[i] = Color.FromArgb(
                      (byte)0xFF,
                      (byte)i,
                      (byte)i,
                      (byte)i);
                    }
                    Bmp_1.Palette = imgpal;
                  }
                }
              }
              //----------------------------------------------------------------------------------------------//
            }
          }
    
        }
        else
        {
          break;
        }
      }
      return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }
    
    

    且我寫了一個影像處裡方法imageprocess(),打算要同時呼叫這影像處裡方法來運算出一些數值,

    而我的問題是,

    這個imageprocess()方法的運算量有一點大,但我希望能在短暫的瞬間下就回傳出值,(因為我會取很多張畫面來運算,數值的順序不能錯)

    這樣的情況下有人是建議我用thread來做,

    因此我想請教的部份就是thread寫的動作和位置,

    new的時機、start的位置等等,這些想請各位實際指導一下,

    我有在lock中,也就是上面unsafe括號後試寫這樣:

    Thread imgproc = new Thread(new ThreadStart(this.imageProcessing));
    imgproc.Start();
    TakeImage = false;//複製取像關閉

    結果算出得到數據要非常久,不但CCD的取像畫面會停掉,還只有取像第一次能算出值,之後因CCD畫面停掉就都傳不進OnImage中,

    我改在lock外寫也是一樣

    我相信是我thread這部份寫法有錯,

    想請各位指導一下正確寫法,能即時處理的方式,在此感謝。

     

    第二個問題是重繪的問題,當form上有多開其他的form視窗時,

    會造成顯示CCD動態畫面的panel會消失畫面,或是跳其他視窗時有時也會消失,

    變成背景panel顏色,但CCD還是有在連結的狀態,

    我是有寫:

    private void Form_Main_Paint(object sender, PaintEventArgs e)
    {
                panel1.Refresh();
    }

    情況雖好了一些,但還是不一定,這點想請教是否有要改進的地方

     

    以上兩個問題,還請諸位再指導寫法,謝謝!!

     

     

     

     

    • 已標示為解答 Lolota Lee 2010年7月13日 上午 02:06
    2010年7月11日 上午 04:02
  • 用saveFileDialog儲存圖錯誤

    http://social.msdn.microsoft.com/Forums/zh-TW/233/thread/1e4601d6-104d-4ac1-9b6d-8a95b6435bb8#ba5fc8c6-3792-4617-ad17-1842d6962168

    問題二,用PictureBox取代Panel,宣告Bitmap為Panel大小,畫在Bitmap再指向PictureBox.Image,參考以上心冷大的說明...

     

    2010年7月11日 上午 04:42
  • 謝謝 Joe先生

    問題二是正在try看看,

    至於我後來po的問題一,也就是多執行緒的寫法問題,再請諸位前輩指導一下,

    在此感謝

    2010年7月12日 上午 03:44
  • Using Double Buffering
    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月12日 上午 03:55
    版主
  • Bill 先生您好:

    我看了您給的鏈結,

    大概知道有一個Double Buffering 方法,可去除閃爍的部分

    請問可以再多示範指導一下嗎?

    想知道這方式是如何改善我的兩個問題,

    謝謝您

    2010年7月12日 上午 06:21
  • CodeProject有一篇完整的文章, 就是在說明使用Double Buffer避免閃爍的

    http://www.codeproject.com/KB/graphics/DoubleBuffering.aspx


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月12日 上午 06:31
    版主
  • Bitmap.LockBits 方法 (Rectangle, ImageLockMode, PixelFormat)

    http://msdn.microsoft.com/zh-tw/library/5ey6h79d(VS.80).aspx

    LockBits處理Bitmap,速度應該會快些...

    2010年7月12日 上午 06:38
  • 再請教一下Joe先生與Bill先生

    至於上述中我想請教的第一個問題,也就是即時取像時,我想要將目前這張影像使用我所寫好的影像處理函數來即時運算,

    請問也是要運用Double Buffer的方式嗎?

    因為我的影像處理函式運算量有點大,想要能夠即時運算,希望能在短暫的瞬間下就回傳出值,(因為我會取很多張畫面來運算,數值的順序不能錯)

    這樣是將利用Double Buffer這方法比較適合嗎?

    還是說用thread比較適合呢?

    剛好也想請教用thread去運算他的做法

    再次謝謝

    2010年7月12日 上午 07:14
  • 關於畫面縮放不定時會消失的問題

    我目前先試Joe先生所回覆的"用PictureBox取代Panel,宣告Bitmap為Panel大小,畫在Bitmap再指向PictureBox.Image"

    我實作這部分有個錯誤,請各位看看如下我改的方式

    以下是原廠範例

    public struct CameraInfo
    {
     public GigeApi Camera;
     public GigeApi.StreamCallback OnImage;
     public System.Drawing.Rectangle outRectangle;
     public Graphics gpanel;
    };
    delegate void SetStatusBarCallBack();
    private CameraInfo[] Camera;
    
    
    //按鈕:連接兩支CCD
    private void btn_CCD_Discover_Click(object sender, EventArgs e)
    {
     //第一支CCD
     Camera[0].gpanel = panel1.CreateGraphics();
     Camera[0].Camera = new GigeApi();
     Camera[0].OnImage = new GigeApi.StreamCallback(this.OnImage);
     Camera[0].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
     Camera[0].Camera.RegisterImageCallback(Camera[0].OnImage);
     Camera[0].Camera.Initialize(SN_Camera0, 3, 0);
    
     //第二支CCD
     Camera[1].gpanel = panel2.CreateGraphics();
     Camera[1].Camera = new GigeApi();
     Camera[1].OnImage = new GigeApi.StreamCallback(this.OnImage);
     Camera[1].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
     Camera[1].Camera.RegisterImageCallback(Camera[1].OnImage);
     Camera[1].Camera.Initialize(SN_Camera0, 3, 0);
    }
    
    
    //繪至影像的方法
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
     GigeApi.tcImageInfo info = new GigeApi.tcImageInfo();
     
     while (true)
     {
      bool ret = Camera[Context].Camera.GetImage(ref info);
    
      if (ret)
      {
       lock (info.bitmap)
       {
        lock (Camera[Context].gpanel)
        {
         Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);       
        }
       }
    
      }
      else
      {
       break;
      }
     }
     return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }

     

    而以下是我改的部分

    public struct CameraInfo
    {
      public GigeApi Camera;
      public GigeApi.StreamCallback OnImage;
      public System.Drawing.Rectangle outRectangle;
      public Graphics gpanel;
      public Bitmap gBmp;//我新加的部分
    };
    
    //===========================================================================================//CCD 副函式
    
    
    public GigeApi.SVSGigeApiReturn OnImage(int Image, int Context)
    {
      GigeApi.tcImageInfo info = new
    
      GigeApi.tcImageInfo();
    
      while (true)
      {
        bool ret = Camera[Context].Camera.GetImage(ref info);
    
        if(ret)
        {
          lock(info.bitmap)
          {
            //新加入的寫法
            if(Context == 0)//第一支CCD
            {
              lock(Camera[Context].gBmp)
              {
                Camera[Context].gBmp = info.bitmap;
                pictureBox1.Image = Camera[Context].gBmp;
              }
            }
    
            if(Context == 1)//第二支CCD
            {
              lock(Camera[Context].gBmp)
              {
                Camera[Context].gBmp = info.bitmap;
                pictureBox2.Image = Camera[Context].gBmp;
              }
            }
    
            //lock (Camera[Context].gpanel)//原本的寫法,但畫面卻會不固定消失
            //{
            // Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);
            //}
          }
        }
        else
        {
          break;
        }
    
      }
      return GigeApi.SVSGigeApiReturn.SVGigE_SUCCESS;
    }
    
    
    //初始化按鈕
    private void btn_CCD_Discover_Click(object sender, EventArgs e)
    {
      string SN_Camera0 = "7097";
      string SN_Camera1 = "6806";
    
      Camera[0].gpanel = panel1.CreateGraphics();
      Camera[0].gBmp = new
    
      Bitmap(640, 480);//新加的
    
      Camera[0].Camera = new GigeApi();
      Camera[0].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[0].outRectangle = new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height);
      Camera[0].Camera.RegisterImageCallback(Camera[0].OnImage);//查RegisterImageCallback
      Camera[0].Camera.Initialize(SN_Camera0, 3, 0);
      Camera[0].Camera.SetFrameRate(200);
      Camera[0].Camera.SetExposureTime(5000);
    
      if (Camera[0].Camera != null)
        Camera[0].Camera.Live();
    
      Camera[1].gpanel = panel2.CreateGraphics();
      Camera[1].gBmp = new Bitmap(640, 480);//新加的
      Camera[1].Camera = new GigeApi();
      Camera[1].OnImage = new GigeApi.StreamCallback(this.OnImage);
      Camera[1].outRectangle = new System.Drawing.Rectangle(0, 0, panel2.Width, panel2.Height);
      Camera[1].Camera.RegisterImageCallback(Camera[1].OnImage);//查RegisterImageCallback
      Camera[1].Camera.Initialize(SN_Camera1, 3, 1);
      Camera[1].Camera.SetFrameRate(200);
      Camera[1].Camera.SetExposureTime(5000);
    
      if (Camera[1].Camera != null)
        Camera[1].Camera.Live();
    }

     

     

    我解釋一下我改的部分,原本範例是用panel1和panel2來顯示畫面,

    然後在callback的方法OnImage中,用Camera[Context].gpanel.DrawImage(info.bitmap, Camera[Context].outRectangle);繪入

     

    而我是新增了picturebox1和picturebox2,並在結構CameraInfo中新增了 Bitmap gBmp;成員,目的是希望在方法OnImage中用來接收info.bitmap

    並直接在OnImage中指給picturebox1或picturebox2.image ,(因為有兩支不同的ccd,我在方法OnImage去用參數Context區分目前畫面是給誰的)

    執行後,會出現兩種問題

    一是有時是兩支都在動作,但過一會就會停住畫面,就不會更新了,

    二是過不久就直接在picturebox.image那一行跳出 "其他地方正在使用物件"的訊息,

    在此請教各位,是否我的寫法中有何盲點或沒注意到的地方要改的呢?

    在此感謝

     

     

     

     

    • 已編輯 Tenmo 2010年7月12日 下午 01:52 程式碼亂碼,重貼一次
    2010年7月12日 上午 11:07
  • 試試在Load 事件委派函式加上, 會不會能改善閃爍

     

         Me
    
    
    .SetStyle(ControlStyles
    
    
    .AllPaintingInWmPaint, True
    
    
    )
            Me .SetStyle(ControlStyles .DoubleBuffer, True )

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月15日 上午 03:19
    版主