Paralel Programlama

PC tabanlı görüntü işleme uygulamalarında, paralel programlama teknikleri kullanmak çok radikal hız artışları sağlayabilir. Yazılımımız, gerçekten sahip olduğumuz donanımın hakkın veriyor mu? Günden güne hızlanan bilgisayarlar, çok çekirdekli işlemciler, devasa hafızalar… Bu kaynakları gerçekten etkin olarak kullanabiliyor muyuz?

Düzgün yönetilmememiş paralel programlama (Multi threaded programming) uygulamalarında çoğu kez olan yukarıdaki gibidir 🙂 Bilmem kaç çekirdekli cep telefonlarının neden bu kadar yavaş olduğu sorusunun da cevabı? Tamam çekirdek çok da, uygulama onları nasıl kullanıyor?

Paralel programlama derken tam olarak neyi kastediyoruz?

Paralel programlama, bilgisayar işlemcisinde gerçekleşen işlemlerin, birinin diğerini beklemediği, aynı anda bir çok işlemin çalıştığı uygulamalardır. Visual Studio .NET dilinden konuşursak, çok kanallı programlar (Multiple threads), parallel tasks, background worker v.b. yapılar ile çoğu programcının bir şekilde aşina olduğu yapılardır. Bu yapılar ile uğraşanlar iyi bilir, duruma göre tam bir başağrısıdırlar. Geliştirmesi zor, debug etmesi zor, sonuçların ekranda gösterilmesi bile ayrı bir teknik gerektirir. Lock objects ler, autoreset, manual reset eventler, mutexler, semaphore lar v.b. .NET yapıları, durduk yere hiç bir programcının severek bulaşacağı yapılar değildir, kabul edelim.


Eğer öyle ise, görüntü işleme uygulamam gerçekten paralel çalışmalı mı?
Bu  sorunun cevabı, eğer zaman sorunum varsa, ya da birim sürede çok daha fazla kontrol etmem gerekiyorsa EVET, aksi halde HAYIR dır.

Gerçek hayattan örnek vermek gerekirse, Derby traş bıçakları fabrikasında bir makinada kurulu olan sistemimiz, her 600 milisaniyede bir, üretilen traş bıçağını kontrol etmektedir. Böyle bir sistemde,

  • Kameradan Görüntünün Alınması (80 ms.)
  • Alınan Görüntünün İşlenmesi (60 ms)

sürmektedir. Özetle, tetik sinyali geldikten sonra, en geç 150 ms. sonra, OK ya da NOK cevabını sisteme bildirmekteyiz. Geri kalan 450 ms. içinde de sistem diğer ürünün gelmesini beklemektedir. Dolayısıyla, bir zaman sorunu yoktur, işlemleri paralel yapmak kimseye bir şey kazandırmayacaktır. Bu durumda, eski tip, ardışıl programlama (görüntü al, işle, dijital çıkış ver, bekle, başa dön..) en doğru seçenek olacaktır.

Peki ya sürekli ve çok hızlı kontrol etmem gerekirse, mesela araçlardaki şerit takip sistemi örneği gibi. Sistem sürekli ve olabildiğince hızlı görüntü almalı, işlemeli ve hiç beklemeden yeniden görüntü almalı, yeniden işlemeli. bu alma-işleme süreci ne kadar kısa olursa, sistem o kadar sağlıklı / gerçeğe yakın çalışacaktır.

Yine gerçek bir örnek verelim;

Halı Kesme makinası olarak kurulumunu yaptığımız sistemde 13 adet kamera vardır. 50 m/dk hızıyla üretilen halıları dikey olarak tam kesim çizgisinden kesmek istiyoruz. Bunun için 13 bıçağın, kameralar yardımıyla anlık olarak sağa-sola yönlendirilmesi ve tam çizgi üzerinden kesim yapılması sağlanmalıdır.

Halı, esnek olduğundan, kesim çizgisi sağa sola kayabilmekte ve ancak kamera yardımıyla çizgiyi görüp takip etmesi sağlanabilmektedir. Sorun şu ki, 50 m/dk. hızında üretim yapılırken, 120 ms. içinde halı 10 cm kaymaktadır. Dolayısıyla, bu projede, tipik olarak 1. kameradan görüntü al, işle, 2. ye geç al işle, sonra 3. ye… şeklinde bir yaklaşım izlenmiş olsa, algoritmalar ne kadar düzgün çalışırsa çalışsın, sonuç tam bir felaket olacaktır. Tek bir kameradan görüntü alınıp yine aynı kameraya sıra gelene kadar, halı 1 m. den fazla ilerleyecektir ve böyle bir sistem elbette kabul edilemez.

HALCON 12 ile Paralel Programlama

Visual Studio kullanarak geliştireceğimiz, çok kameralı tipik bir paralel programlama algoritması genel hatları ile şu şekildedir.

3 adet genel thread vardır.

  1. Grabbing Threads : Var olan kamera sayısı kadar thread açılır. Her bir thread sonsuz bir döngüde görüntü alır. Diyelim ki 4 kamera var. 4 ü de eş zamanlı olarak görüntü alır. Biri diğerini beklemez, her biri bağımsız olarak kendi görüntüsünü alır.
  2. Processing Thread : Yine kamera sayısı kadar thread açılır. Her bir thread, sonsuz bir döngü içinde, kendi kamerasından görüntü alınmışsa, bunu işler yine başa döner (yeni görüntü alınmasını bekler) görüntüyü işlemeye başladığı anda, grabbing thread yeniden resim alma işi ile meşgul olur. Diyelim ki, resim alma (grabbing) 60 ms, işleme ise 20 ms sürsün. bu durumda, tek bir kamera 60 ms içinde sonucu verecektir (80 değil) çünkü 20 ms. işleme sürerken, grabbing thread görüntü almaya başlamıştı ve işleme bittikten 40 ms. sonra (60 değil) yeni görüntü gelecektir.
  3. User Interface Thread : Benim kullanmadığım ama genel amacı, bulunan sonuçların ekranda gösterilmesi, label buton panel v.b. form kontrollerinin çizilmesi amacıyla kullanılır. Ben, HALCON fonksiyonlarına, DisplayHandle gibi bir değişken gönderiyorum ve ekran işlemlerini de HALCON içinden yaptırıyorum. Böylece, sonuçların toparlanması ekranlarda gösterilmesi gibi işleri C# tarafında değil, HALCON tarafında hallediyorum.

Bu yapı, kendi içinde gayet hızlı çalışacak ve CPU kullanımı tüm çekirdeklerde %100 e vuracaktır. bu iyiye işarettir, tek bir cpu birimini boşa harcamaya gerek yoktur ve uygulamamız resmen donanımın hakkını veriyor demektir.

Ayrıca, bellek kullanımına dikkat edin, sonsuz döngüde çalışan thread (kanal) yapıları, nesneleri hafızadan atamıyorsa, çok çabuk hafıza dolabilir. İdealde hafızanın sabit kalması gerekmektedir.

HALCON 12 ile Paralel Programlama

Visual Studio ile, paralel programlama tekniklerini etkin bir şekilde kullanarak, çok kameralı sistemde, tüm görüntü alma ve işleme kanallarını paralel çalıştırarak, çok büyük bir hız avantajı sağladık. Şimdi HALCON ile bu yapıyı daha da geliştirmek mümkün.

Öncelikle HALCON, tecrübeli olmayan programcılar ya da .NET çok kanallı yapılarını sevmeyen tercih etmeyen kullanıcılar için bir çok yenilik barındırmaktadır. Artık tüm paralel programlama işlemlerini HALCON kendisi yerine getirebilmektedir. HALCON12, yazdığınız kod parçalarını (HDevelop procedure) tek bir tıklama ile paralel olarak çalıştırabilmektedir. (Bizim C# ile tabir yerinde ise, o kadar kastırarak yaptığımız dümenlerin hepsi boşa mı gitti şimdi 🙁 )

Ve hala daha da fazlasını sunmaktadır. O da şu :

diyelim ki, her bir kameradan alınan görüntüde, tek bir işlem değil de, birden fazla işlem yapmak durumundayım. Mesela alınan görüntüde hem barkod, hem karekod, hem de OCR yapmak durumundayım diyelim.

Barkod bulma 40 ms, OCR 90 ms, karekod okuma ise 70 ms. civarı sürsün.

HDevelop içinde bu işi yapan 3 güzel procedure yazdığımı varsayalım. HALCON 12, bu 3 procedure ü paralel olarak başlatabilmekte (par_start komutu ile) Buraya kadar olan, zaten tecrübeli C# kullanıcılarının daha önceden yaptığına denk gelen kısım. HALCON, par_join komutu ile, bu 3 procedure ü de aynı anda çalıştırmakta, en son hangisi biterse, diğerleri onu beklemekte, ve tek bir yapı çalıştırmışım gibi sonlanmakta. Dolayısıyla, bu yapıları paralel çalıştır diyorum, sanki hepsi tam da aynı anda çalışıp aynı anda bitmiş gibi, bir alt satırda da hepsinin sonuçlarını toplayabiliyorum. Bizim örneğimizde, 90 ms. sonunda barkod, karekod ve OCR işlemim bitmiş, elimde hazır sonuçlar olmuş oluyor.

Bu güçlü yapıyı, C# daki özgün multi thread yapım ile birleştirdim. Ortaya, C# tarafında, tüm kameralardan görüntü alma ve işleme işlemlerinin paralel koşturulduğu, HALCON tarafında da bir çok procedure (alt programı) aynı anda çalıştırıldığı, hibrit bir yapı çıktı. Sistemi 13 kamera için çalıştırdığımda, cpu fanı hemen devreye girmekte CPU maksimum performansa zorlanmakta. (Bu aşamada i7 gibi işlemcilerin gerçek farkını ve hızını hissedebilmek de bir programcı olarak ayrı bir mutluluk kaynağı oluyor.)

Peki, neden aynı anda bir çok procedure start edilir. Ya da ne gibi uygulamalar buna ihtiyaç duyar.

Açıkçası bu çok sık karşılaşılan bir yapı veya ihtiyaç değildir.

Bizim halı kesme makinasında, siztem kesim çizgisini aramaktadır. Yazdığım algoritmalarda bazen var_threshold ile sonuca gitmiş, bazen de binary_threshold işe yaramıştı. Bazı durumlarda median_rect filtresi güzel sonuçlar vermiş, bazen ise label_to_region işe yaramıştı. Bazen GMM classification imdada yetişmiş, bazen resmi decompose edip hsv kanallarına bakmam gerekmişti?

İyi ama, bunların hangisini kullanmam en doğrusuydu? Apaçık ki, hiç biri tek başına yeterli değil. Hepsini tek tek denesem, bu sefer çok uzun sürebilir. İşte tam da bu anda, yazdığım tüm alt programları (ki aslında hepsinin derdi aynı, hepsi de çizgi arıyor) aynı anda start ediyorum. Resmi grab etmem (kameradan almam) ortalama 60 ms. kadar sürüyor (bendeki pixel clock, exposure değerleri ile inebildiğim minimum değer) ve bir sonraki resim gelene kadar, tüm yapıları HALCON aynı anda start ediyor. bunlardan bir veya daha fazlası sonucu zaten buluyor ve bulduğum sonucu işliyorum.

Sonuç?

Eğer bu kadar fazla multitasking sonucu yazdığınız kodlar halıya çıkmadıysa, iyi yoldasınız demektir.

Yazının Özeti :

HALCON 12 güçlü bir multithreading (paralel programlama) desteği sunmaktadır. par_start, par_join, mutex, message queue yapılarını inceleyin, örneklere bakın, dokümanları okuyun, çok yardımcı olacaktır. Buna rağmen danışmanız gereken bir yer olursa, buradan ya da info@mavis.com.tr ile bana istediğiniz zaman ulaşabilirsiniz.

kolay Gelsin 😉

Resim Boyutu Hesaplama

iDS çok daha yüksek hassasiyette renk bilgisi verebilen, 12 bit derinlikli RGB36 kameraları (ve yeni SDK) pazara sundu. Renk derinliğinin 8 bitten 12 bite yükseltilmesi (RGB24 ten RGB36 ya) alınacak her bir resmin boyutunun da değişmesine neden oldu.

Mavis olarak geliştirdiğimiz bu Excel tablosu ile (indirmek için tıklayınız), farklı çözünürlüklerdeki kameralar için resim boyutunu hesaplayabilirsiniz.

 

Plastik parçalar üzerindeki yüzüklerin kontrolü

Otomotiv sektöründe hizmet veren bir firma için yüzük kontrol sistemini devreye aldık. Sistemde 3 farklı ürün bulunmaktadır. Her bir üründe yüzüğün takılacağı yerler ve renkler farklılık göstermektedir. Bu ürünler modellenerek kontrol edilmesi istenen ürünlerin doğru veya hatalı olarak ayrıştırılması sağlanmıştır.

Operatör tarafından kullanıcı girişi yapılabilen, kontrol edilecek modelin listeden seçilebileceği ve sistemin çalıştırılması veya durdurulması gibi işlemleri içeren bir arayüz tasarlanmıştır.

Sistem, konveyör sistemi üzerinde bölmelere ayrılmış bir bandın ilerlerken mekanik sensör yardımıyla adım adım durdurulması, fotoğraf çekilmesi, bu fotoğrafın bilgisayar üzerindeki yazılım ile işlenerek ürünlerin doğru veya hatalı olarak tespit edilmesi, bu bilgilerin ekranda gösterilmesi ve farklı kovalara atılması işlemlerinden oluşmaktadır.

Kontrol edilecek ürünler farklı pozisyonlarda geleceği için bu ürünlerin ve yüzüklerin aranacağı bölgelerin modellenen ürünlere göre döndürülmesi gerekmektedir. Bu işlemi sağlayan HALCON kodu:

// Kontrol edilecek ürünün fotoğrafının okunması
read_image (Image, 'C:/Projects/M1_175740.bmp')
// Matching(Eşleştirme) için kullanılacak modelin okunması
read_shape_model ('C:/Projects/P1-N.shm', ModelId)
//  Matching(Eşleştirme) işlemi
find_shape_model (Image, ModelId, rad(0), rad(360), 0.5, 1, 0.5,['least_squares','max_deformation 2'], [7,1],0.75,ModelRow, ModelColumn, ModelAngle,ModelScore)
    if (ModelScore > 0.75)
        get_shape_model_contours (ModelContours, ModelId, 1)
        hom_mat2d_identity (HomMat)
        hom_mat2d_rotate (HomMat, ModelAngle, 0, 0, HomMat)
        hom_mat2d_translate (HomMat, ModelRow, ModelColumn, HomMat)
        affine_trans_contour_xld (ModelContours, TransContours, HomMat)
        gen_rectangle1 (ModelRegion, 396.643, 361.944, 816.643, 1134.52)
        area_center (ModelRegion, ModelRegionArea, RectificationRow, RectificationCol)
        hom_mat2d_identity (Rectification)
        hom_mat2d_translate (Rectification, RectificationRow-ModelRow, RectificationCol-ModelColumn, Rectification)
        // Ürünün döndürülmesi
        hom_mat2d_rotate (Rectification, -ModelAngle, RectificationRow, RectificationCol,Rectification)
        affine_trans_image (Image, RectifiedImage, Rectification, 'constant', 'false')
    endif

Doğru ve hatalı olarak tespit edilen ürünlerin ekran görüntüleri :

partition_dynamic kullanımı

Bir region üzerinde işlem yaparken sık sık connection komutu ile region da kaç adet nesne olduğu ile ilgileniriz. connection komutu, birbirinden bağımsız nesneleri seçeceği için, nesneler arasında 1 pixel lik dahi bağlantı olsa, onu tek bir nesne olarak algılayacaktır. Özellikle OCR işlemi gibi, her nesnenin ayrı olarak değerlendirilmesi gereken durumlarda, bu problemi gidermek gerekir. Yollardan biri partition_dynamic komutunun kullanılmasıdır.

 

Yukarıdaki resimde görüldüğü gibi, 1 ve 2 ile 6 ve 7 arasını birer çizgi ile birleştirdim. Burada threshold komutu ile bir region oluşturduğumda ve hemen sonra connection ile her bir rakamı seçmek istediğimde,

yukarıdaki gibi bir seçim yapacaktır. (1-2 ve 6-7 nin tek bir nesne olarak seçildiğine dikkat edin)

partition_dynamic komutu tam da bu gibi durumlarda istenen sonuçları üretebilir. partition_dynamic, nesneleri dikey olarak bölmeye yarayan bir komuttur. Ortalama bir nesnenin genişliğini belirtip, belirli bir yüzde toleransı da verdiğimizde, komut nesleneri olması gerektiği yerden parçalayacaktır. (Aşağıdaki resimdeki gibi)

 

partition_dynamic komutuna verilecek, ideal nesne genişliği ne olmalıdır?

nesne genişliğini komutla ya da HDevelop içindeki Feature Inspection aracı ile görebiliriz.

Yukarıdaki resimde görüldüğü gibi, seçtiğim nesnenin özelliklerini (features) gözlemledim. İlgilendiğim width değerinin 85 olduğunu gördüm. Programda direk olarak bu değeri kullanabilirim.

Dilersem daha dinamik bir yapı kurarak, olması gereken değeri kendim belirlerim. Aşağıdaki kod, hem sabit olarak bu 85 değerini kullanan, hem de dinamik olarak kendi belirlediği değere göre (tuple_median) çalışan HDevelop kodudur.

 

read_image (Image, 'C:/Users/Mustafa/Desktop/Adsız.png')
threshold (Image, Regions, 0, 139)
connection(Regions, ConnectedRegions)
* Sabit 85 değerine göre partition
partition_dynamic(ConnectedRegions, Partitioned, 85, 20)

*kendi belirleyeceği değere göre partition
smallest_rectangle1(ConnectedRegions, Row1, Column1, Row2, Column2)
W := Column2 - Column1
tuple_median (W, WOrta)
partition_dynamic(ConnectedRegions, Partitioned, WOrta, 20)

OCR Engine İncelemeleri ve HALCON OCR

Sözkonusu olan, endüstriyel görüntü işleme uygulamaları ise, elbette HALCON, herhangi bir OCR engine ile kıyaslanamayacak yeteneklere sahiptir. Bununla birlikte, sözkonusu olan Form (Doküman) OCR işlemi ise, o zaman diğer alternatiflere de bir göz atmak ta fayda olabilir (mi?)

İncelemelerime HALCON dışında, yaygın olarak bilinen farklı ürün ile başladım. Abbyy FineReader Engine 10.0, Google Tesseact OCR, Microsoft Office Document Imaging.

Bu yazıda her iki engine OCR engine, yetenekleri ve HALCON ile kıyaslamasını yapmayı düşünüyorum.

Abbyy FineReader ile başlayalım. Şüphesiz OCR denince ilk akla gelen yazılım. Yılların tecrübesine sahip bu Ukrayna yazılımı, aldığı sayısız ödül sayesinde beklentilerimizi en üst seviyede tutmamızı sağladı.

Artıları :

  • Eksiksiz Profesyonel Bir Ürün
  • Çok İyi Dökümantasyon
  • Tüm Geliştirme Ortamlarını Desteklemesi
  • Kolay Kurma, Öğrenme
  • Blurry, skewed (bulanık, dönük) metinleri OCR edebilme
  • Form Alanı Tanımlayabilme (Region)
  • Mobile Platform desteği
  • Geniş çok dilde dictionary (lexicon) desteği

Eksileri

  • Çağdışı ve çok uzun lisanslama süreci
  • Yüksek lisanslama ücretleri ve lisanslama politikası
  • Yavaş OCR etmesi

Denemek için bile olsa, FineReader kurmak istiyorsanız, öncelikle kötü bir niyetinizin olmadığını, aksi halde akla hayale gelmeyecek kadar çok para cezasına çarptırılacağınızı kabul ettiğiniz dökümanları eksiksiz doldurup, fax, mail, posta, kargo (şaka yapmıyorum) ile hem Ukrayna, hem de Türkiye ofislerine göndermelisiniz. Eğer başvurunuz kabul edilirse, size bir ftp şifresi verilecek ve ürünün 2 aylık deneme versiyonunu indirmeye hak kazanacaksınız.

Sonuçta, FineReader free form (döküman) tipi işlemlerde kabul edilebilir bir OCR kalitesi sunmaktadır. Eğer hızlı çalışmak gibi bir beklentiniz yoksa, sonuç ta gayet tatminkardır.

Ters dönmüş belgeler, çizginin üzerine yazılmış alanlar gibi durumlarda başarılı olamadı (ki bu kadarını beklemiyordum zaten)

TesserAct

Sponsorunun google olması, ürüne ciddi eğilmemi sağladı. Çok kısa download (5.8 MB) sonrası hemen kurup denemeye başlayabiliyorsunuz.

Artıları

  • Sponsored By Google
  • Open source (dolayısıyla çokça geliştirici)
  • Oldukça küçük ve hızlı
  • Çoklu dil desteği
  • Ücretsiz olması

Eksileri

  • Arkasında ciddi bir firma olmaması
  • Karmaşık dökümantasyon (wiki style)
  • Henüz tam oturmamış izlenimi vermesi
  • Web platform desteği
  • Her geçen gün daha da gelişmesi

TesserAct ile yaptığım denemelerde, FineReader kadar yüksek skorlar elde edememekle birlikte, yakın sonuçlar yakaladım. Uygulamanın ciddi bir bütçesi olmayacaksa, TesserAct iyi bir seçenek olabilir.

Ben kişisel olarak, Önce HALCON ile formu iyice temizleyip yine HALCON ile OCR etmeyi düşünüyorum. HALCON ile OCR işlemi yeterince başarılı olamamışsa, o zaman okunamayan yerleri TesserACT ile yeniden deneteceğim. Müşterinin uygulamaya (ve OCR edilecek her sayfaya) bütçe ayırabilmesi durumunda, FineReader elbette daha ciddi bir yaklaşım olacaktır.

MODI (Microsoft Office Document Imaging), Office 2007 ile birlikte ücretsiz olarak gelen, ScanSoft menşeli yazılım.

Artıları

  • Microsoft Desteği
  • Office 2007 ile ücretsiz gelmesi
  • Kullanışlı Arayüz ve Yazılımlarla tam entegrasyon
  • .NET kütüphanesi ile uyum

Eksileri

  • Artık Desteklenmemesi !
  • FineReader kadar yüksek skorlarla OCR edememesi

Aslında MODI, en kolay kurulup ve uygulama geliştirmeye müsait hale gelen seçenek. Buna rağmen artık desteklenmemesi ciddi bir soru işareti oluşturuyor ve şimdilik tarafımca rafa kaldırılıyor.

(Bu konuya yine devam edeceğim)

 

 

 

uEye SDK kullanarak görüntünün Bitmap olarak alınması

iDS uEye Kameraları, güçlü bir SDK ile gelmektedir. Bu SDK, görüntünün kameradan düzgün bir şekilde alınabilmesi içindir. uEye Cockpit programında elle yapılan tüm işlemlerin kod ile (program) yapılabilmesi içindir. (Pozlama, kazanç, pixel clock, frame rate vs…)

Eğer uEye SDK düzgün olarak yüklenmişse, makinanızda IDS klasörü altında resimdeki gibi, tüm gerekli dosyalar klasörler olacaktır. Buradaki örneklere ve yardım dosyalarına göz atmakta fayda vardır. Unutulmamalıdır ki, uEye SDK, bir görüntü işleme kütüphanesi değildir. (bir HALCON değil mesela)

Bize .NET, Directshow, ActiveX ve yaygın görüntü işleme kütüphaneleri için interface ler sunar. Yani ben sana görüntüyü veririm, sen al ne yaparsan yap der.

Bazı iDS uEye kullanan müşterilerimiz, görüntüyü bitmap olarak almak ve bu bitmap olarak alınmış görüntüde pixel-pixel kendi algoritmalarını geliştirmek istemektedirler. Her ne kadar iç içe for döngüleri ile pixel-pixel çalışmak hayli zahmetli ve ilkel bir yöntem olsa da, çok basit ve zaman sorunu olmayan görüntü işleme uygulamalarında iş görüyor olabilir. Dolayısıyla, tüm bu yazının özeti, bize sık sorulan, iDS uEye kameralar kullanarak görüntüyü bitmap olarak nasıl alırım sorusuna bir cevap yazalım.

1 .NET interface kullanmak

Object Oriented mantığına göre geliştirilmiş .NET interface kullanmak en doğru yöntemdir.

Başlangıç olarak, uEye_DotNet_Simple_Live isimli C# uygulamasını açın. En temel fonksiyonları ve kullanımını basitçe göstermektedir.  Bunu çalıştırın. Herşey yolunda ise, çok küçük bir ekleme yapalım.

Bir PictureBox komponeti koyalım. (Bitmap nesnesini bunun üzerine atayacağız)

Şimdi Freeze butonuna aşağıdaki kodları ekleyelim.

            uEye.Defines.Status statusRet = 0;

            // Get last image memory
            Int32 s32LastMemId;
            Int32 s32Width;
            Int32 s32Height;
            statusRet = Camera.Memory.GetLast(out s32LastMemId);
            statusRet = Camera.Memory.Lock(s32LastMemId);
            statusRet = Camera.Memory.GetSize(s32LastMemId, out s32Width, out s32Height);

            Bitmap MyBitmap;
            statusRet = Camera.Memory.ToBitmap(s32LastMemId, out MyBitmap);

            // unlock image buffer
            statusRet = Camera.Memory.Unlock(s32LastMemId);

            pictureBox_bmp.SizeMode = PictureBoxSizeMode.StretchImage;
            pictureBox_bmp.Image = MyBitmap;

Görüldüğü gibi, Freeze() ile kamerayı video modundan çıkartıp stil image (sabit görüntü alma) moduna getirdim, ve kamera üzerindeki aktif görüntüyü bir bitmap değişkenine aldım. Sonra da onu pictureBox1 nesnesine atadım (siz dilediğiniz gibi kullanabilirsiniz)

Freeze() ile görüntüyü durdurabilir, Freeze(100) gibi bir komutla, 100 ms bekletebilirsiniz.

Ethernet kamera kullanıyorsanız, GetActive() , GetLast() vs deneyebilirsiniz. (Aktif olan görüntüyü değil de en sonuncusunu ver vs.)

ya da, henüz yeni görüntü oluşmamışsa (frame_rate 10 ise, 100 ms. de 1 yeni görüntü oluşacaktır gibi) bu durumda

Camera.Acquisition.Freeze(100)

gibi bir komutla, 100 ms beklemesini isteyebilirim. Uygulamaya göre değişir. sonuçta elimde Bitmap türünden görüntü mevcuttur artık.

2. dll kullanarak Bitmap olarak alma

(.NET çıkmadan önce yazdığım eski bir makaleydi bu. Orijinal haliyle bırakıyorum, .NET varken kullanılmasını çok da tavsiye etmem ama hala iş görür)

SDK ve uEye_api dll kullanımı için, Program Files altındaki IDS klasöründeki yer alan uEye_CSharp_Demo güzel bir örnek oluşturmaktadır. Burada, kameradan alınan görüntü (canlı ya da tek bir kare) .NET PictureBox bileşeni üzerinde gösterilmektedir. görüntüyü direk olarak PictureBox bileşeninin Handle ına atamak yerine Bitmap nesnesi olarak almak (ve işlemek) için, aşağıdaki kod kullanılabilir.

        private void button1_Click(object sender, EventArgs e)
        {
            Bitmap MyBitmap;

            // get geometry of uEye image buffer
            int width = 0, height = 0, bitspp = 0, pitch = 0, bytespp = 0;
            long imagesize = 0;
            m_uEye.InquireImageMem(m_pCurMem, GetImageID(m_pCurMem), ref width, ref height, ref bitspp, ref pitch);
            bytespp = (bitspp + 1) / 8;
            imagesize = width * height * bytespp; // image size in bytes

            // bulit a system bitmap
            MyBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);

            // fill the system bitmap with the image data from the uEye SDK buffer
            BitmapData bd = MyBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            m_uEye.CopyImageMem(m_pCurMem, GetImageID(m_pCurMem), bd.Scan0);
            MyBitmap.UnlockBits(bd);

            //picShow.Image = MyBitmap;
            pictureBox2.Image = MyBitmap;

            //Bitmap may be saved to file if necessary
            MyBitmap.Save("uEyeImage.bmp");
        }

3. ActiveX kullanmak

ActiveX, VB6 ya da Delphi kullanıcısı iseniz mantıklı olabilir .Her ne kadar 2014 sonundan itibaren desteklenmese de (yeni versiyon ve bug fix vs gelmeyecektir),  iDS tarafından hala yürürlüktedir. En iyisi görüntüyü Save etmek, sonra Bitmap olarak load etmektir.

4. Görüntü İşleme Kütüphanesi kullanmak

Eğer HALCON kullanıyorsanız, kamerayı direk tanıyacak ve grab_image() komutu kameradan görüntüyü alıp size verecektir.

Defne Yaprağı Ayrıştırmada Matematiksel Yaklaşım

Daha önceki blog kayıtlarımızda sıklıkla bahsettiğimiz Hatalı Defne Yaprağı Ayrıştırma Projemize tamamen matematiksel yöntemlerle çalışan, daha hızlı ve daha güvenilir sonuçlar veren yeni bir yaklaşım ekledik.

Matematikte Saddle Point (Semer Noktası) olarak bilinen fonksiyonun,  defne yapraklarına uyarlanması temelinde yapılan işlemler, daha kısa sürede ve daha hızlı sonuçlar vermektedir. Görüntü işleme fonksiyonları ile matematiksel fonksiyonların eş zamanlı olarak kullanıldığı bu yeni yaklaşımda, işlemci (cpu) hızı artırıldıkça, doğru orantılı olarak işlemin sonlanma hızı da artmaktadır. (Salt görüntü işleme fonksiyonları kullanılsaydı, işlemci hızının artışı yine olumlu sonuçlar verecekti fakat işlemcide geçmeyen süreler – kameralardan görüntünün alınması, veriyolunda kaybedilen zamanlar, hafızaya okuma/yazma süreleri vb. – yüzünden beklenen hız artışı daha sınırlı kalacaktı.)

 

 

 

 

 

 

 

Yukarıdaki resimde, sağlam ve hatalı yaprağın, üst yarısının kenar çizgileri gösterilmiştir. İdeal bir yaprakta (hatasız) semer noktası (saddle point) hiç olmamalıdır.

Analiz Yöntemi :

  1. Yaprak yatay düzlem ile sıfır derece açı yapacak şekilde döndürülür
  2. Yaprağın alt ve üst bölgelerinin konturları (contours) çıkartılır
  3. Çıkartılan konturlara ait, satır-sütun değerleri fonksiyonel olarak çıkartılır
  4. Oluşturulan 2 boyutlu fonksiyonda semer noktaları (saddle points) aranır
  5. Semer noktaları derinliği belirlenen değerlerden fazla ise yaprak hatalıdır
  6. Yaprak yatay eksenle 90 derece açı yapacak şekilde döndürülür (Sol ve Sağ tarafı baş aşağı gelecek şekilde)
  7. Tüm kontroller yeniden yapılır.

Tüm bu işlemlerde kullanılan belli başlı HALCON operatörleri :

  • Contour processing (edge-detection, smooth contours, union contours…)
  • 1d fonksiyonel işlemleri (create_funct_1d_pairs, derivate_funct_1d  …)
  • Semer Noktası işlemleri (saddle_points_sub_pix …)

Bu kez çalışan HALCON kodu örneği yerine kullanılan fonksiyonları ve yöntemi açıkladım. Konuyla ilgilenenlerin olması ve ulaşması durumunda çalışan gerçek kodlar ile daha detaylı paylaşımlarda bulunabiliriz.

 

USB Modül Kurulum ve Kullanımı

%100 Açık kaynak kodlu, Mavis USB I/O sınıfının kullanımı:

VS içinden projenizi açıp, Mavis tarafından tedarik edeceğiniz USB_DIO ve DioModule sınıfını projenize eklendiğinizde Solution Explorer içinde soldakine benzer bir görüntü olması lazım.

USB_DIO, HID USB fonksiyonlarının olduğu bir kütüphanedir. Genel USB giriş çıkış fonksiyonlarının tamamını destekler.

DIOModule ise, genel USB fonksiyon setinin Mavis tarafından geliştirilmiş giriş çıkış modülüne uyarlanmış halidir. Byte se Bit seviyesinde okuma yazma yapabilen ve modülün giriş çıkış işlemlerine olanak sağlayan yapıları içerir.

Bu aşamadan sonra, normal C# sınıf kullanımı gibi DIOModule kullanımı yapılabilir.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.IO;

namespace VYP
{    

    public partial class frmMain : Form
    {

		public DIOModule dioModule = new DIOModule();

bu tanımlama ile dioModule isminde global bir değişkenimiz oldu.

Bir trigger yardımıyla sürekli okuma

		private void tmrDIO_Tick(object sender, EventArgs e)
		{
			if ((dioModule.connected) && (dioModule.Active))
			{
				byte inputDIO;
				dioModule.ReadDio(out inputDIO, false);
                if (dioModule.DIChanged())
                {
					if (inputDIO == dioModule.triggerSignal)
					{
						//RunAll();
						// Put Here your trigger code...
					}
                }
			}
		}

DIOModule kullanarak belirli bir çıkış değerini set etme

if ((dioModule.Active) && (dioModule.connected))
{
dioModule.WriteDio(1, false); // Led leri yakıyoruz...
}

DIOModule kullanarak girişteki değeri okuma

if ((dioModule.Active) && (dioModule.connected))
{
byte inputDIO;
dioModule.ReadDio(out inputDIO, false);
// okunan değer inputDIO değişkenine alındı
}

dioModule Pin bazında erişim imkanı da sunmaktadır. Indexer yapısıyla desteklenmiş bu kullanım moduna göre

dioModule.SetPin(2);

dioModule.SetPin(4);

gibi yapı kullanılabilir. Bunun yerine daha gelişmiş olan;

dioModule.Pins[2] = 1;

dioModule.Pins[4] = 0;

gibi, indexer yapısı da tercih edilebilir.

Belirli bir pin değerini okumak için;

dioModule.GetPin(2);

yapısı

veya

byte b = dioModule.Pins[2]; indexer yapısı tercih edilebilir.

Belirli bir pin değerini sıfırlamak için

dioModule.ClearPin(2);

yapısı kullanılabilir. Bu yapıya göre, diğer çıkış değerlerinin ne olduğuna bakılmaksızın 2 nolu pin sıfıra çekilecektir.

Mavis, dioModule için gelişmiş fonksiyon setine sahiptir. Daha fazla bilgi için bizimle irtibata geçiniz…

Delphi XE yeni bir şans olabilir mi?

Delphinin en son versiyonu olan Delphi XE (RAD Studio XE) raflarda yerini aldı. Programın 30 günlük deneme sürümünü kurup, yeni (ya da daha da geliştirilmiş) özelliklerine göz attığımda Delphi XE nin gerçekten son derece gözalıcı özelliklerle donatıldığını gördüm. 64 bit derleyiciler hariç, hemen hemen her başlıkta eksiksiz bir ürün çıkartılmış.

Bunlara başlıklara kısaca değinirsek

Derleyici : En büyük değişim, delphi derleyicisinin ANSI C++ derleyicisi ile çok daha uyumlu hale getirilmiş olmasında görülmektedir. Yeni nesil işlemciler (SSE4.x, AMD 3DNow vs.) için uyumluluk ve artırılmış performanstan bahsedebiliriz. Derleme işlemi artık arkaplanda bir thread tarafından yapılabiliyor. (Çok büyük projelerde, saatler süren derlemeleriniz varsa, işe yarar bir özellik şüphesiz)

Geliştime Ortamı : Artık Delphi geliştiricileri Visual Studio 2008 / 2010 veya MonoDevelop içinden geliştirme yapabilecekler. (Radikal bir değişiklik olmakla birlikte kimin bu şekilde kullanacağını da merak ediyorum. Çok değil, birkaç sene önce IDE savaşlarından bahsedenler şimdi en güçlü silahlarını almadan savaş meydanına mı çıkıyor?) Birde beklendiği üzere, Win32 SDK, Windows 7 API ve .NET 4.0 da tam desteklenmektedir.

Dilde Yapılan Yenilikler : Kişisel olarak en çok beğendiğim özellik, Exit derken artık opsiyonel olarak Result değerini de set edebiliyoruz. (C# taki return false, return 3 vb. kullanım gibi) Bunların dışında, RegEx (Regular Expression) library kullanımı artık – çok geç kalınmıl olsa da- mümkün. Dosyalama ve I/O işlemlerini yapmak için kullanılan eski tarz fonksiyon bazlı yapı artık OOP çatısına kavuşturulmuş. Yine C++ uyumlu object sınıfının ToString metodları, Unicode destekleri ve TStringBuilder gibi (yine C# tan aşina) sınıflar gelmiş (DateTime tipi ve DateUtils uniti geliştirilmiş mesela)

Kod Yönetimi : Tanıtımlarda ve ürün reklamlarında üstüne basa basa vurguladıkları Subversion Integration (versiyon, kodda yapılan değişiklikler ve geçmiş kontrolü) gelmiş. (Bir çok üründe ezelden beridir var olan bir özellik. Kaldı ki, 10 yıl önceki Delphi 7  Team Edition bile buna yakın özellikler sunuyordu zaten) Kod düzenleyici (Kod Formatlayıcı) , Refactoring (değişken rename vb. işlemler) gibi araçlar daha da desteklenmiş.

Modelleme : Son zamanlarda yazılım mühendisliğinin moda kavramlarından olan Modelling de unutulmamış. Nesne yapısı hiyerarşisinin gösteriminden, durum grafiklerine, komponent diyagramlarından, kod dökümanı oluşturmaya kadar aklınıza ne geliyorsa fazlası var, eksiği yok deyip geçeyim.

Test : Yine zorla her programa dayatılan Unit Testing den sonunda Delphi de nasibini almış. DUnit gibi süper bir unitimiz var artık.

Veritabanı : Sanırım her bir yeni versiyon için en çok ter döktükleri yer burası. BDE den ADO ya, dbExpress ten dbGO ya, önce tonla farklımetodoloji icat edersen sonunda geleceğin nokta budur. Özetle, adı geçen tüm veritabanı erişim ve sunum teknolojilerinin tamamı güncellenmiş. BDE (Borland Database Engine) hala güncel olduğunu görünce nostalji etkisinden gözlerim yaşardı desem yalan olmaz. Ürünle birlikte Interbase 20 kullanıcılı Developer Edition ücretsiz gelmekte (Yine ilgililere – varsa – duyurulur)

Yeni Teknolojiler : Cloud (Bulut) desteği de unutulmamış. Amazon EC2 ile patlayan, Microsoftun her zamanki gibi ışığı görüp çakmasını çıkartıp bende varım dediği (Azure) tam olarak desteklenmiş (Google Cloud neden desteklenmemiş acaba.)

Komponentler : Delphiyi delphi yapan VCL ler, 3rd party componentler… Burada önce hakkını verelim, son derece seçkin component setleri hazır olarak gelmekte. Indy, Raize, IP-Works, TeeChart, Nevrona Rave, Installaware gibi çok seçkin componentler hazır. DevExpress gibi UI alanında bestseller bir ürünün hala yüklü gelmemesi ise bence büyük hata. (Üstelik Delphi kökenli DevExpress cileri böylesine küstürmek te büyük başarı)

Web : Yine Delphi tarafından sayısız deneme yanılma yapılan bir diğer konu. Atozed firmasının Intraweb komponentleri XE ile birlikte de geliyor. VCL olarak kullanmak için ideal bir set.  Indy bileşenleri de eksiksiz, güçlü, open source  bir alternatif. WebSnap kullananlar için Server Side VBScript JavaScript desteği eklenmiş.

Yeni eklenen komponentlere (VCL) gelince;  etkileyici Gesture designer gelmiş. dokunmatik ya da etkileşimli uygulama yazanlar bunu çok sevecekler. TTouchKeyboard (Sanal Ekran Klavyesi) de yeterince başarılı. Görsel olarak çok futuristik olmasa da, kullanımı kolay ve fazlasıyla iş görüyor.

(Web geliştirme konusunda Delphi Prism / RadPHP XE ilgili (ve iddialı) olduğundan WEB tabanlı projeleri bu ürünlerle değerlendirmek daha sağlıklı bir yaklaşım olur)

Sonuç :

90 ların ortasında çıkan, kısa sürede efsaneleşen Delphi ne yazık ki, son 4-5 yıldır sürekli kan kaybetmektedir. Bu kötü gidişatın sebepleri ayrı bir makale konusu olacağından girmek istemiyorum. Delphi XE, ilk bakışta gözalıcı görünmekle birlikte, kaçan delphi geliştiricilerini geri getirebilecek mi? Ya da diğer dilleri (Mesela Visual Basic, C#, Java) kullanarak geliştirme yapan programcıları kazanabilecek mi? Her ikisine de evet diyebilmek çok zor. Eğer eldeki delphi geliştiricilerini kaçırmazsa bu bile büyük başarı olacaktır.

Delphi neredeyse her yıl yeni bir versiyon çıkartmaktadır. CodeGear RAD Studio 2009, Embarcadero Delphi 2010, Şimdi RAD Studio XE… Buradan da anlaşılacağı gibi, geçiş süreci hala devam etmektedir. Şimdilik görünen, Delphi nin var olan teknolojileri çok geçmeden yakaladığı ve kendi geliştiricilerini her zaman up to date tuttuğudur. Burada insanüstü bir gayret gösterildiğini anlamak zor değil. Microsoft cephesinde, işler çok daha yalın. Herşeyden önce işletim sistemi ve Framework gibi kendi tekelinde ürünler var. Piyasaya yeni bir versiyon çıkmadan önce, kendi içinde (inhouse) olarak tüm birimler eşzamanlı harekete geçebilir.  Her birim kendi uyumlu güncellemelerini yapabilir. Delphi geliştiricileri ise, oturup final release beklemek zorundalar. Tek beklenilen Microsoft olsa yine iyi, App Engine alanında gittikçe daha çok görülen Google, Java dünyası, Açık kaynak kodu geliştiriciler vs. vs. Hemen her cephede en önde yer alıp şaşkın şaşkın dolaşmaktan başka çareleri yok gibi…

Ben kişisel olarak Visual Studio ve C# kullanıyorum. Delphi kullanmayı bırakalı 3-4 yıl oluyor. Hala bazı projelerde Delphi ile çözüm geliştirdiğim de oluyor (100 lerce MB framework kurmayı gerektirmeyen, Native Win32 uygulamalarında mesela…)

Delphi geliştiricilerinin Delphi yi bırakmaları için hala ciddi bir neden yok. Benzer şekilde Delphi de ısrar etmeleri için de bir neden yok.

Delphi yıllar önce Anders Hejlsberg ile yaptığını bir daha yapabilir mi? Üzülerek te olsa pek ihtimal veremiyorum. Bana göre Borland firması, Hejlberg’in Microsofta 1 milyon $ ve hisse ortaklığı sonucu kaptırılmasıyla zaten bitmişti. Sonrasında firma  sayısız isim değiştirmiş ve son olarak 2 yıl önce tüm ürünleri ile birlikte komik ötesi bir rakamla Embarcadero’ya  satılmıştı. Tüm satış değeri sadece 23 milyon $ dı. (Borland’ın alıcı bulmak için kapı kapı gezdiği yıllarda, Türkiye’den bir alıcı çıksa Türk Bilişim sektörü için çağın hareketi olurdu diye iç geçirirdim. Emsal teşkil etmesi açısından bazı örnekler vermek istiyorum;

  • Neredeyse tedavülden kalkacak olan Sybase veritabanı, SAP ye, Borlandın tam 250 katına, 5.6 milyar dolara satıldı.
  • Ülkemizde Delphi kullanarak geliştirme yapan Logo Yazılımın Borsa bedeli 70 milyon $ civarında
  • Ülker, Belçikalı çikolata üreticisi Godiva’yı 600 milyon $a satın aldı
  • GS nin Aslantepe projesi : 300 milyon $ (bunun 4 katına çıkacağı söyleniyor)
  • BİM grubunun Mecidiyeköydeki eski likör fabrikasına verdiği teklif : 450 milyon $)

Sonuçta, yeni projeler için birincil uygulama olarak seçmemekle birlikte, eski gözağrımı, 1. versiyonundan 7. versiyonuna kadar kullandığım Delphi yi, arada bir göz hapsinde tutmakta fayda var 😉 )

Barkod Printer, Seri Port, USB, C# üzerine… – 3

Bundan önceki ilk 2 makalede sırasıyla Barkod yazıcılara ve C# içinden erişime değinmiştik. Ülkemizde yaygın olarak bulunan Zebra ve Argox yazıcılar için, barkod komut dili olan PPLA PPLB ZPL gibi diller ile tasarım yapmayı ve bu tasarımları barkod yazıcıya göndermeyi anlatmıştım.

Bu makalede ise  C# içinden  USB ile bağlı olan yazıcıya, data göndermek üzere gliştirilmiş kütüphaneyi ve C# içinden kullanımını göstereceğim.

aşağıdaki kodu DirectPrint.cs olarak kaydedip, var olan projenize item olarak eklemeniz yeterlidir.

// DirectPrint.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace VYP
{
	public class RawPrinterHelper
	{
		// Structure and API declarions:
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
		public class DOCINFOA
		{
			[MarshalAs(UnmanagedType.LPStr)]
			public string pDocName;
			[MarshalAs(UnmanagedType.LPStr)]
			public string pOutputFile;
			[MarshalAs(UnmanagedType.LPStr)]
			public string pDataType;
		}
		[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

		[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool ClosePrinter(IntPtr hPrinter);

		[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

		[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool EndDocPrinter(IntPtr hPrinter);

		[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool StartPagePrinter(IntPtr hPrinter);

		[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool EndPagePrinter(IntPtr hPrinter);

		[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
		public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

		// SendBytesToPrinter()
		// When the function is given a printer name and an unmanaged array
		// of bytes, the function sends those bytes to the print queue.
		// Returns true on success, false on failure.
		public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
		{
			Int32 dwError = 0, dwWritten = 0;
			IntPtr hPrinter = new IntPtr(0);
			DOCINFOA di = new DOCINFOA();
			bool bSuccess = false; // Assume failure unless you specifically succeed.

			di.pDocName = "Mavis RAW Document (Barcode)";
			di.pDataType = "RAW";

			// Open the printer.
			if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
			{
				// Start a document.
				if (StartDocPrinter(hPrinter, 1, di))
				{
					// Start a page.
					if (StartPagePrinter(hPrinter))
					{
						// Write your bytes.
						bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
						EndPagePrinter(hPrinter);
					}
					EndDocPrinter(hPrinter);
				}
				ClosePrinter(hPrinter);
			}
			// If you did not succeed, GetLastError may give more information
			// about why not.
			if (bSuccess == false)
			{
				dwError = Marshal.GetLastWin32Error();
			}
			return bSuccess;
		}

		public static bool SendFileToPrinter(string szPrinterName, string szFileName)
		{
			// Open the file.
			FileStream fs = new FileStream(szFileName, FileMode.Open);
			// Create a BinaryReader on the file.
			BinaryReader br = new BinaryReader(fs);
			// Dim an array of bytes big enough to hold the file's contents.
			Byte[] bytes = new Byte[fs.Length];
			bool bSuccess = false;
			// Your unmanaged pointer.
			IntPtr pUnmanagedBytes = new IntPtr(0);
			int nLength;

			nLength = Convert.ToInt32(fs.Length);
			// Read the contents of the file into the array.
			bytes = br.ReadBytes(nLength);
			// Allocate some unmanaged memory for those bytes.
			pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
			// Copy the managed byte array into the unmanaged array.
			Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
			// Send the unmanaged bytes to the printer.
			bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
			// Free the unmanaged memory that you allocated earlier.
			Marshal.FreeCoTaskMem(pUnmanagedBytes);
			return bSuccess;
		}

		public static bool SendStringToPrinter(string szPrinterName, string szString)
		{
			IntPtr pBytes;
			Int32 dwCount;
			// How many characters are in the string?
			dwCount = szString.Length;
			// Assume that the printer is expecting ANSI text, and then convert
			// the string to ANSI text.
			pBytes = Marshal.StringToCoTaskMemAnsi(szString);
			// Send the converted ANSI string to the printer.
			SendBytesToPrinter(szPrinterName, pBytes, dwCount);
			Marshal.FreeCoTaskMem(pBytes);
			return true;
		}
	}

}

VS içindeki Projeniz şuna benzer bir şekilde görünür…

Artık, nereden isterseniz erişebileceğiniz çok düzgün bir sınıfımız oldu. SendBytesToPrinter içinde, yazdırma yöneticisine gönderilecek döküman adı bilgisini görebilirsiniz. Siz barkod bastır komutu verdiğinizde, Windows yazdırma yöneticinde bu isim görünecektir. Dolayısıyla projenize uygun şık bir isim girebilirsiniz.

Programınızdan kullanımı ise, bir önceki makalede gösterildiği gibidir. Yine de kısaca tekrar etmek gerekirse

RawPrinterHelper.SendStringToPrinter(printerName, barStrings);

şeklindeki bir yapı işinizi görür. Buradaki printerName, windows sürücüleri ile kurulu yazıcınızın adıdır. barstrings ise göndereceğiniz barkod komutlarıdır.

Benzer şekilde, paralel port kullanan nokta vuruşlu yazıcılara da direk USB üzerinden aynı sınıfı kullanarak erişebilirsiniz.