Baumer GAPI ve OpenCV

Yazılım örneği Baumer GAPI v2.9.0

Bu kılavuzda, Baumer GAPI çerçeve tamponlarından görüntüleri alıp, bunları daha fazla işleme için OpenCV’ye nasıl aktaracağınızı öğreneceksiniz.

Baumer kamerayı kurmak ve görüntüleri belleğe almak için Baumer GAPI SDK’sını kullanacağız. Daha sonra bellekteki görüntüleri OpenCV’ye aktararak sabit diske kaydedeceğiz. OpenCV’ye aktarıldıktan sonra, kütüphaneyi kullanarak görüntüleri ihtiyaçlarınıza göre daha fazla işleyebilirsiniz.

Görüntüleri Baumer GAPI’den OpenCV’ye aktarma

Baumer GAPI’de görüntü verilerini aldıktan sonra, bu veriler aşırı yüklenmiş yapıcı (constructor) kullanılarak bir OpenCV matrisi (Mat) ile ilişkilendirilebilir:

                
new cv::Mat::Mat(int _rows, int _cols, int _type, void * _data, size_t _step = 0Ui64);
                 
            
                
new Emgu.CV.Mat.Mat(int rows, int cols, Emgu.CV.CvEnum.DepthType type, 
                    int channels, IntPtr data, int step);
                 
            

Baumer GAPI bellek arabelleğindeki veriler kopyalanmaz, bunun yerine bir işaretçi OpenCV matrisine geçirilir. OpenCV matrisi, ardından cv::imwrite() kullanılarak diske kaydedilebilir. Kaydettikten sonra, OpenCV matrisini silebilir veya yeniden kullanabilir ve Baumer GAPI belleğini yeniden sıraya alarak yeni bir görüntü yakalamak için kullanabilirsiniz.

Baumer GAPI arabelleğini OpenCV matrisine klonlama, kopyalama ve dönüştürme

Baumer GAPI Bellek verileriyle çalışmak için OpenCV’nin Clone(), Copy() veya ConvertTo() yöntemlerini kullanabilirsiniz. Kopyalama işleminden sonra, Baumer GAPI Belleği yeniden sıraya alınabilir ve kopyalanan veriler OpenCV kullanılarak daha fazla işlenebilir.

Dikkat edilmesi gereken nokta, OpenCV matrisinin genişlik, yükseklik ve tür (CV_8UC1, CV_8UC3, CV_16UC1, CV_16UC3) bilgilerini içerirken, Baumer GAPI belleğinin Buffer.FrameID, Buffer.PixelFormat, Buffer.XOffset, Buffer.YOffset gibi ek yararlı bilgiler yanı sıra parça (chunk) verileri de sağladığıdır. Gerekirse bu bilgileri de kopyalamanız gerekebilir.

                
pDevice->GetRemoteNode("PixelFormat")->SetString("Mono8");

BGAPI2::Buffer* pBufferFilled = pDataStream->GetFilledBuffer(1000);
if (pBufferFilled->GetPixelFormat() == "Mono8")
{
    cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(), 
                                      (int)pBufferFilled->GetWidth(), 
                                      CV_8UC1, 
                                      (char *)pBufferFilled->GetMemPtr());

    // 3 methods to copy
    cv::Mat imClone = imOriginal->clone();

    cv::Mat* imCopy = new cv::Mat((int)pBufferFilled->GetHeight(), 
                                  (int)pBufferFilled->GetWidth(), 
                                  CV_8UC1);
    imOriginal->copyTo(*imCopy);

    cv::Mat* imConvert = new cv::Mat((int)pBufferFilled->GetHeight(), 
                                     (int)pBufferFilled->GetWidth(), 
                                     CV_8UC1);

    imOriginal->convertTo(*imConvert, CV_8UC1, 1.0);

    delete imOriginal;
    pBufferFilled->QueueBuffer();

    // Use copied images
    cv::imwrite("cv_image_Clone.png", imClone);
    cv::imwrite("cv_image_Copy.png", *imCopy);
    cv::imwrite("cv_image_Convert.png", *imConvert);

    delete imCopy;
    delete imConvert;
}
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "Mono8";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if (mBufferFilled.PixelFormat == "Mono8")
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv8U, 1, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 1);

    // 3 methods to copy
    Emgu.CV.Mat imClone = imOriginal.Clone();

    Emgu.CV.Mat imCopy = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                         (int)mBufferFilled.Width, 
                                         Emgu.CV.CvEnum.DepthType.Cv8U, 1);
    imOriginal.CopyTo(imCopy);

    Emgu.CV.Mat imConvert = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                            (int)mBufferFilled.Width, 
                                            Emgu.CV.CvEnum.DepthType.Cv8U, 1);
    
    imOriginal.ConvertTo(imConvert, Emgu.CV.CvEnum.DepthType.Cv8U, 1.0);

    mBufferFilled.QueueBuffer();

    // Use copied image
    Emgu.CV.CvInvoke.Imwrite("cv_image_Clone.png", imClone);
    Emgu.CV.CvInvoke.Imwrite("cv_image_Copy.png", imCopy);
    Emgu.CV.CvInvoke.Imwrite("cv_image_Convert.png", imConvert);
}
                 
            

PixelFormat’ları Anlama

Baumer kameralar, Mono8, Mono12, BGR8 gibi çeşitli PixelFormat’ları destekler ve bunların, ilgili OpenCV formatlarıyla eşleştirilmesi gerekir. Aşağıdaki tabloda, eşleşen OpenCV Matris Türlerini görebilirsiniz.

Baumer formatı OpenCV formatı
Mono8 CV_8UC1, (8 bit, 1 kanal)
Mono10 Mono16’ya dönüştür (bit-kaydırma)
Mono12 Mono16’ya dönüştür (bit-kaydırma)
Mono16 CV_16UC1, (16 bit, 1 kanal)
BGR8 CV_8UC3, (8 bit, 3 kanal)
BGR10 BGR16’ya dönüştür (bit-kaydırma)
BGR12 BGR16’ya dönüştür (bit-kaydırma)
BGR16 CV_16UC3, (16 bit, 3 kanal (BGR))
RGB8 cv::cvtColor kullanarak BGR8’e dönüştür
RGB10 BGR16’ya bit-kaydırma ve cv::cvtColor ile dönüştür
RGB12 BGR16’ya bit-kaydırma ve cv::cvtColor ile dönüştür
RGB16 cv::cvtColor kullanarak BGR16’ya dönüştür
BayerGB8, BayerRG8, BayerGR8, BayerBG8 cv::cvtColor kullanarak BGR8’e dönüştür
BayerGB10, BayerRG10, BayerGR10, BayerBG10 BGR16’ya bit-kaydırma ve cv::cvtColor ile dönüştür
BayerGB12, BayerRG12, BayerGR12, BayerBG12 BGR16’ya bit-kaydırma ve cv::cvtColor ile dönüştür

Dönüşüm gerektirmeyen PixelFormat’lardaki görüntülerle çalışmak oldukça basittir, aşağıdaki örneklerde bunu görebilirsiniz.

Örnek: Mono8

                
pDevice->GetRemoteNode("PixelFormat")->SetString("Mono8");

BGAPI2::Buffer * pBufferFilled = pDataStream->GetFilledBuffer(1000);
if (pBufferFilled->GetPixelFormat() == "Mono8")
{
	cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(),
	                                  (int)pBufferFilled->GetWidth(),
	                                  CV_8UC1,
	                                  (char *)pBufferFilled->GetMemPtr();
	cv::imwrite("cv_Mono8_image.png", *imOriginal);
	delete imOriginal;
}
pBufferFilled->QueueBuffer();
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "Mono8";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if (mBufferFilled.PixelFormat == "Mono8")
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv8U, 1, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 1);
    Emgu.CV.CvInvoke.Imwrite("cv_Mono8_image.png", imOriginal);
}
mBufferFilled.QueueBuffer();
                 
            

Örnek: BGR8

OpenCV’deki standart renkli PixelFormat, her renk kanalı için 8 bit olan BGR formatıdır (CV_8UC3). Kamerada bu formata uyması için PixelFormat’i BGR8 olarak ayarlayabilirsiniz (BGR8Packed de kullanılabilir).

                
pDevice->GetRemoteNode("PixelFormat")->SetString("BGR8");

BGAPI2::Buffer * pBufferFilled = pDataStream->GetFilledBuffer(1000);
if ((pBufferFilled->GetPixelFormat() == "BGR8") || 
    (pBufferFilled->GetPixelFormat() == "BGR8Packed"))
{
    cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(),
                                      (int)pBufferFilled->GetWidth(),
                                      CV_8UC3,
                                      (char *)pBufferFilled->GetMemPtr());
    cv::imwrite("cv_BGR8_image.png", *imOriginal);
    delete imOriginal;
}
pBufferFilled->QueueBuffer();
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "BGR8";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if ((mBufferFilled.PixelFormat == "BGR8") ||
    (mBufferFilled.PixelFormat == "BGR8Packed"))
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv8U, 
                                             3, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 3);
    Emgu.CV.CvInvoke.Imwrite("cv_BGR8_image.png", imOriginal);
}
mBufferFilled.QueueBuffer();
                 
            

PixelFormat’ları 16 Bit OpenCV Matris Tipine Dönüştürme

Bazı kameralar 12 bit (Mono12, RGB12) gibi PixelFormat’ları destekler. Bu verileri 16 bit OpenCV Matris Tipinde saklamak, beklenenden çok daha karanlık bir çıktı-görüntüsüne yol açabilir. 12 bit PixelFormat’ları 16 bit OpenCV Matris Tipine eşlemek için, Baumer GAPI bellek verilerinin 4 bit kaydırılması (16 ile çarpılması) gerekir. Bu işlem OpenCV ConvertTo() komutu kullanılarak yapılabilir.

Benzer şekilde, 10 bit PixelFormat’ları (örneğin Mono10), verileri 2 bit kaydırarak veya 64 ile çarparak dönüştürülebilir.

Uyarı: Baumer GAPI bellek verilerine doğrudan bir işaretçi geçirildiği için, orijinal veri değiştirilir.

                
pDevice->GetRemoteNode("PixelFormat")->SetString("Mono12");

BGAPI2::Buffer * pBufferFilled = pDataStream->GetFilledBuffer(1000);
if (pBufferFilled->GetPixelFormat() == "Mono12")
{
    cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(),
                                      (int)pBufferFilled->GetWidth(),
                                      CV_16UC1,
                                      (char *)pBufferFilled->GetMemPtr());

    imOriginal.ConvertTo(imOriginal, Emgu.CV.CvEnum.DepthType.Cv16U, 64.0);
    cv::imwrite("cv_Mono12_as_Mono16_image.png", *imOriginal);
    delete imOriginal;
}
pBufferFilled->QueueBuffer();
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "Mono12";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if (mBufferFilled.PixelFormat == "Mono12")
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv16U, 
                                             1, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 2);
    
    imOriginal.ConvertTo(imOriginal, Emgu.CV.CvEnum.DepthType.Cv16U, 16.0);
    Emgu.CV.CvInvoke.Imwrite("cv_Mono12_as_Mono16_image.png", imOriginal);
}
mBufferFilled.QueueBuffer();
                 
            

Bayer PixelFormat’ları ile Çalışma

Bayer deseni, CCD ve CMOS kameralarında yaygın olarak kullanılır. Bu desen, R, G ve B piksellerinin (belirli bir bileşenin sensörleri) aşağıdaki resimde gösterildiği gibi birbirine geçecek şekilde tek bir düzlemden renkli görüntüler elde etmenizi sağlar.

Bir pikselin çıkış RGB bileşenleri, aynı renge sahip 1, 2 veya 4 komşu pikselden enterpolasyon yoluyla elde edilir. Yukarıdaki desenin, deseni bir piksel sola ve/veya bir piksel yukarı kaydırarak elde edilebilecek birkaç modifikasyonu vardır. CV_Bayer C1C2 2BGR ve CV_Bayer C1C2 2RGB dönüşüm sabitlerindeki iki harf C1 ve C2, belirli desen türünü gösterir. Bunlar sırasıyla ikinci satırdaki ikinci ve üçüncü sütunlardaki bileşenlerdir. Örneğin, yukarıdaki desen çok popüler bir “BG” türüne sahiptir.

OpenCV’de ‘cvtColor’ Kullanarak Renk Dönüşümü

Renkli kameralar, BayerBG8, BayerGB8, BayerGR8 ve BayerRG8 gibi ham PixelFormat’ları içerir. Bu formatların, daha fazla görüntü işleme için BGR8 formatına dönüştürülmesi gereklidir.

Baumer kameraları, Bayer desenlerini görüntünün ilk satırındaki ilk iki piksele göre adlandırır. Buna karşılık, OpenCV adlandırması, görüntünün ikinci satırındaki ikinci ve üçüncü piksele bağlıdır, bu da farklı bir adlandırmaya yol açar:

  • Baumer BayerGB OpenCV’de BayerGR ile eşleşir.
  • Baumer BayerRG OpenCV’de BayerBG ile eşleşir.
  • Baumer BayerGR OpenCV’de BayerGB ile eşleşir.
  • Baumer BayerBG OpenCV’de BayerRG ile eşleşir.
                
pDevice->GetRemoteNode("PixelFormat")->SetString("BayerRG8");

BGAPI2::Buffer * pBufferFilled = pDataStream->GetFilledBuffer(1000);
if (pBufferFilled->GetPixelFormat() == "BayerRG8")
{
    cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(),
                                      (int)pBufferFilled->GetWidth(),
                                      CV_8UC1,
                                      (char *)pBufferFilled->GetMemPtr());
    cv::Mat* imTransformBGR8 = new cv::Mat((int)pBufferFilled->GetHeight(),
                                           (int)pBufferFilled->GetWidth(),
                                           CV_8UC3);

    //Baumer: RGrgrg  >>  OpenCV: rgrgrg
    //        gbgbgb              gBGbgb
    cv::cvtColor(*imOriginal, *imTransformBGR8, CV_BayerBG2BGR); //to BGR

    delete imOriginal;
    pBufferFilled->QueueBuffer();

    cv::imwrite("cv_BayerRG8_as_BGR8_image.png", *imTransformBGR8);
    delete imTransformBGR8;
}
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "BayerRG8";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if (mBufferFilled.PixelFormat == "BayerRG8")
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv8U, 1, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 1);
    Emgu.CV.Mat imTransformBGR8 = new Emgu.CV.Mat();

    //Baumer: RGrgrg  >>  OpenCV: rgrgrg
    //        gbgbgb              gBGbgb
    Emgu.CV.CvInvoke.CvtColor(imOriginal, imTransformBGR8, 
                              Emgu.CV.CvEnum.ColorConversion.BayerBg2Bgr);

    mBufferFilled.QueueBuffer();

    Emgu.CV.CvInvoke.Imwrite("cv_BGR8_image.png", imTransformBGR8);
}
                 
            

Bayer’den BGR16’ya Dönüşüm

Bazı ham PixelFormat’lar, Bayer görüntüsünde daha fazla bit kullanır, örneğin BayerBG12, BayerGB12, BayerGR12 ve BayerRG12. Bu ham verilerin önce 12 bitten 16 bite dönüştürülmesi gerekir. Ardından, 16 bit değer cvtColor kullanılarak 48 bit formatında (her renk kanalı için 16 bit) BGR16’ya dönüştürülebilir.

10 bit formatlar (örneğin, BayerBG10, BayerGB10, BayerGR10 veya BayerRG10) için ise bunları 16 bit Bayer formatına dönüştürmek amacıyla 16.0 yerine 64.0’lık bir ölçekleme faktörü kullanmalısınız.

                
pDevice->GetRemoteNode("PixelFormat")->SetString("BayerGB12");

BGAPI2::Buffer * pBufferFilled = pDataStream->GetFilledBuffer(1000);
if (pBufferFilled->GetPixelFormat() == "BayerGB12")
{
    cv::Mat* imOriginal = new cv::Mat((int)pBufferFilled->GetHeight(),
                                      (int)pBufferFilled->GetWidth(),
                                      CV_16UC1,
                                      (char *)pBufferFilled->GetMemPtr());
    cv::Mat* imConvert = new cv::Mat((int)pBufferFilled->GetHeight(), 
                                     (int)pBufferFilled->GetWidth(), 
                                     CV_16UC1);
    //convert with scaling of 16.0 to convert 12-Bit to 16-Bit 
    imOriginal->convertTo(*imConvert, CV_16UC1, 16.0);

    delete imOriginal;
    pBufferFilled->QueueBuffer();

    cv::Mat* imTransformBGR16 = new cv::Mat((int)pBufferFilled->GetHeight(), 
                                            (int)pBufferFilled->GetWidth(), 
                                            CV_16UC3);

    //Baumer: GBgbgb  >>  OpenCV: gbgbgb
    //        rgrgrg              rGRgrg
    cv::cvtColor(*imConvert, *imTransformBGR16, CV_BayerGR2BGR); //to BGR
    delete imConvert;

    cv::imwrite("cv_BayerGB12_as_BGR16_image.png", *imTransformBGR16);
    delete imTransformBGR16;
}
                 
            
                
mDevice.RemoteNodeList["PixelFormat"].Value = "BayerGB12";

BGAPI2.Buffer mBufferFilled = mDataStream.GetFilledBuffer(1000);
if (mBufferFilled.PixelFormat == "BayerGB12")
{
    Emgu.CV.Mat imOriginal = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                             (int)mBufferFilled.Width, 
                                             Emgu.CV.CvEnum.DepthType.Cv16U, 1, 
                                             (IntPtr)mBufferFilled.MemPtr, 
                                             (int)mBufferFilled.Width * 2);
    Emgu.CV.Mat imConvert = new Emgu.CV.Mat((int)mBufferFilled.Height, 
                                            (int)mBufferFilled.Width, 
                                            Emgu.CV.CvEnum.DepthType.Cv16U, 1);

    //convert with scaling of 16.0 to convert 12-Bit to 16-Bit 
    imOriginal.ConvertTo(imConvert, Emgu.CV.CvEnum.DepthType.Cv16U, 16.0);

    mBufferFilled.QueueBuffer();

    Emgu.CV.Mat imTransformBGR16 = new Emgu.CV.Mat();

    //Baumer: GBgbgb  >>  OpenCV: gbgbgb
    //        rgrgrg              rGRgrg
    Emgu.CV.CvInvoke.CvtColor(imConvert, imTransformBGR16, 
                              Emgu.CV.CvEnum.ColorConversion.BayerGr2Bgr);

    Emgu.CV.CvInvoke.Imwrite("cv_BayerGB12_as_BGR16_image.png", imTransformBGR16);
}
                 
            

İndirmeler

Yazılım Örnekleri


Baumer GAPI ve OpenCV

C++ (ZIP | 9 KB)

C# (ZIP | 10 KB)