Anda di halaman 1dari 8

Algoritma Card Detection Langkah pertama untuk melakukan proses card recognition adalah dengan mendeteksi benda kartu/card

pada citra. Untuk mendeteksi kartu, digunakan beberapa filter citra yakni Grayscaling dan thresholding pada citra. Ini ditujukan untuk mendapatkan gambar hitam dan putih sehingga memudahkan untuk mendeteksi tepian dari kartu. Algoritma yang digunakan yakni:
Bitmap temp = source.Clone() as Bitmap; //menduplikasi citra agar citra yang asli tetap ada FiltersSequence seq = new FiltersSequence(); seq.Add(Grayscale.CommonAlgorithms.BT709); //GrayScaling filter seq.Add(new OtsuThreshold()); // thresholding filter temp = seq.Apply(source); // menerapkan filters pada citra

Pada saat telah didapat citra biner, citra kemudian dideteksi dengan metode blob processing melalui class bawaan AForge.Net BlobCounter.
BlobCounter extractor = new BlobCounter(); extractor.FilterBlobs = true; extractor.MinWidth = extractor.MinHeight = 150; extractor.MaxWidth = extractor.MaxHeight = 350; extractor.ProcessImage(temp);

Dengan bantuan class BlobCounter filter maka otomatis citra tampak yang lebar atau tinggi tidak diantara [150,350] pixels akan dihapus. Hal ini membantu kita membedakan kartu dari benda-benda lain (jika ada) dalam citra. Langkah berikutnya, dibutuhkan titik sudut persegi panjang yang terdapat pada citra yang telah melalui proses deteksi kartu. Untuk mencari titik sudut tersebut digunakan fungsi PointsCloud.FindQuadriteralCorners
foreach (Blob blob in extractor.GetObjectsInformation()) { //Get Edge points of card List< IntPoint > edgePoints = extractor.GetBlobsEdgePoints(blob); //Find corners of card on source image from edge points List< IntPoint > corners = PointsCloud.FindQuadrilateralCorners(edgePoints); }

Setelah menemukan sudut kartu, maka dapat mengubah daerah antara sudut dari citra asal ke citra persegi panjang, sehingga kartu dapat diekstrak. Untuk melakukan proses recognition pada kartu, dibutuhkan ukuran kartu yang sama. Namun, ukuran kartu mungkin berbeda karena sudut kamera. Untuk mencegah masalah ini, maka citra kartu diubah menjadi 200 x 300 (pixel).
QuadrilateralTransformation quadTransformer = new QuadrilateralTransformation(); ResizeBilinear resizer = new ResizeBilinear(CardWidth, CardHeight); foreach (Blob blob in extractor.GetObjectsInformation())

{ //mengambil titik tepi dari citra List<IntPoint> edgePoints = extractor.GetBlobsEdgePoints(blob); //mencari sudut kartu dari titik tepi yang ditemukan pada citra List<IntPoint> corners = PointsCloud.FindQuadrilateralCorners(edgePoints); Bitmap cardImg = quadTransformer.Apply(source); if (cardImg.Width > cardImg.Height) cardImg.RotateFlip(RotateFlipType.Rotate90FlipNone); cardImg = resizer.Apply(cardImg); }

Langkah selanjutnya adalah menentukan template matching untuk mencocokan antara citra dengan database/template yang telah disediakan. Digunakan enumerasi sebagai berikut:
public enum Rank { NOT_RECOGNIZED = 0, As = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Ratu, Raja } public enum Suit { NOT_RECOGNIZED = 0, Hati, Wajik, Sekop, Keriting }

Dibuat pula class Card untuk menampilkan kartu yang telah dikenali. Class ini mengandung rank/urutan dari kartu, suit/bentuk dari kartu, gambar dari kartu dan titik sudut dari kartu pada citra asal.
public class Card { //Variables private Rank rank; //Rank of card private Suit suit; //Suit of card private Bitmap image; //Extracted(transformed) image of card private Point[] corners ;//Corner points of card on source image //Properties public Point[] Corners

{ get { return this.corners; } } public Rank Rank { set { this.rank = value; } } public Suit Suit { set { this.suit = value; } } public Bitmap Image { get { return this.image; } } //Constructor public Card(Bitmap cardImg, IntPoint[] cornerIntPoints) { this.image = cardImg; //Convert AForge.IntPoint Array to System.Drawing.Point Array int total = cornerIntPoints.Length; corners = new Point[total]; for(int i = 0 ; i < total ; i++) { this.corners[i].X = cornerIntPoints[i].X; this.corners[i].Y = cornerIntPoints[i].Y; } } }

Berikutnya, untuk mengetahui bentuk yang terdapat di kartu pada citra dilihat dari bentuk standar yakni wajik, keriting, sekop dan hati. Sesuai dengan standar tersebut maka diambil kesimpulan bahwa wajik dan hati berwarna merah sementara keriting dan sekop berwarna hitam. Hal lain yang kita ketahui tentang bentuk adalah bahwa lebar wajik lebih besar dari lebar hati, dan lebar keriting lebih besar dari sekop. Kedua fitur membantu kita mengenali suit/bentuk yang terdapat kartu. Setelah memahami suit/bentuk yang terdapat pada kartu, maka dilanjutkan dengan mengenali warna dominan dalam citra kartu tersebut melalui analisa bentuk pada pojok kanan atas dari citra kartu menggunakan algoritma cropping berikut.
public Bitmap GetTopRightPart() { if (image == null) return null; Crop crop = new Crop(new Rectangle(image.Width - 37, 10, 30, 60)); return crop.Apply(image); }

Setelah melakukan cropping pada ujung kanan atas kartu citra, kita akan mendapatkan ukuran 30x60 pixels. Akan tetapi, terkadang jika kita melakukan cropping pada kartu tersebut seringkali

mengandung urutan/rank dan suit/bentuk. Untuk lebih akurat, kita lakukan cropping lagi dengan mengambil bagian setengah ke bawah dan didapatkan ukuran 30x30pixel Setelah dilakukan cropping, kita dapat menghitung iterasi warna yang dominan dari potongan citra tersebut, jika komponen pixel merah lebih besar daripada jumlah komponen pixel biru dan komponen hijau, maka pixel yang dianggap berwarna merah. Jika komponen pixel merah, hijau, biru lebih kecil dari 50 dan merah tidak lebih besar dari jumlah komponen biru dan komponen hijau, pixel yang dianggap pixel hitam.
char color = 'B'; //Lock Bits for processing BitmapData imageData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); int totalRed = 0; int totalBlack = 0; unsafe { //Count red and black pixels try { UnmanagedImage img = new UnmanagedImage(imageData); int height = img.Height; int width = img.Width; int pixelSize = (img.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; byte* p = (byte*)img.ImageData.ToPointer(); // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize) { int r = (int)p[RGB.R]; //Red pixel value int g = (int)p[RGB.G]; //Green pixel value int b = (int)p[RGB.B]; //Blue pixel value if (r > g + b) //If red is bigger then total of green and blue totalRed++; //then its red if (r <= g + b && r < 50 && g < 50 && b < 50) //If all less than 50 totalBlack++; //then its black } } } finally { bmp.UnlockBits(imageData); //Unlock } } if (totalRed > totalBlack) //If red is dominant color = 'R'; //Set color as Red return color;

Untuk mengetahui apakah kartu adalah wajah atau kartu non-wajah cukup mudah. Kartu wajah memiliki gambar besar pada kartu sedangkan kartu non-wajah tidak. Jika kita menerapkan deteksi

tepi dan pengolahan gumpalan pada kartu dan menemukan gumpalan terbesar pada kartu, kita dapat mengatakan bahwa itu adalah kartu wajah sesuai dengan ukuran gumpalan terbesar.
private bool IsFaceCard(Bitmap bmp) { FiltersSequence commonSeq = new FiltersSequence(); commonSeq.Add(Grayscale.CommonAlgorithms.BT709); commonSeq.Add(new BradleyLocalThresholding()); commonSeq.Add(new DifferenceEdgeDetector()); Bitmap temp = this.commonSeq.Apply(bmp); ExtractBiggestBlob extractor = new ExtractBiggestBlob(); temp = extractor.Apply(temp); //Extract biggest blob if (temp.Width > bmp.Width / 2) //If width is larger than half width of card return true; //Its a face card return false; //It is not a face card } private Suit ScanSuit(Bitmap suitBmp, char color) { Bitmap temp = commonSeq.Apply(suitBmp); //Extract biggest blob on card ExtractBiggestBlob extractor = new ExtractBiggestBlob(); temp = extractor.Apply(temp); //Biggest blob is suit blob so extract it Suit suit = Suit.NOT_RECOGNIZED; //Determine type of suit according to its color and width if (color == 'R') suit = temp.Width >= 55 ? Suit.Diamonds : Suit.Hearts; if (color == 'B') suit = temp.Width <= 48 ? Suit.Spades : Suit.Clubs; return suit; }

lebar = 52 pixels

lebar = 43 pixels

lebar = 47 pixels

lebar = 57 pixels

AForge.NET juga menyediakan kelas untuk pencocokan template yang disebut ExhaustiveTemplateMatching . Kelas mengimplementasikan algoritma template matching lengkap, yang melakukan scan lengkap dari sumber gambar, membandingkan setiap pixel dengan pixel yang sesuai template.
private Suit ScanFaceSuit(Bitmap bmp, char color) { Bitmap clubs, diamonds, spades, hearts; //Suit Templates //Load Templates From Resources , clubs = PlayingCardRecognition.Properties.Resources.Clubs; diamonds = PlayingCardRecognition.Properties.Resources.Diamonds; spades = PlayingCardRecognition.Properties.Resources.Spades; hearts = PlayingCardRecognition.Properties.Resources.Hearts;

//Initialize templateMatching class with 0.8 similarity threshold ExhaustiveTemplateMatching templateMatching = new ExhaustiveTemplateMatching(0.8f); Suit suit = Suit.NOT_RECOGNIZED; if (color == 'R') //If card is red then it can be hearts or diamonds { if (templateMatching.ProcessImage(bmp, hearts).Length > 0) suit = Suit.Hearts; //Check If template matches for hearts if (templateMatching.ProcessImage(bmp, diamonds).Length > 0) suit = Suit.Diamonds; //Check If template matches for diamonds } else //If its black { if (templateMatching.ProcessImage(bmp,spades).Length > 0) suit = Suit.Spades; //Check If template matches for spades if (templateMatching.ProcessImage(bmp, clubs).Length > 0) suit = Suit.Clubs;//Check If template matches for clubs } return suit; }

Selanjutnya dilakukan deteksi urutan dengan cara mengenali kartu wajah and non-kartu wajah secara terpisah. Karena non-kartu wajah dapat dikenali dengan menghitung bentuk yang timbul pada citra kartu tidak dibutuhkan sebuah template matching.
private Rank ScanRank(Bitmap cardImage) { Rank rank = Rank.NOT_RECOGNIZED; int total = 0; Bitmap temp = commonSeq.Apply(cardImage); //Apply filters on image BlobCounter blobCounter = new BlobCounter(); blobCounter.FilterBlobs = true; //Filter blobs whose width or height less than 30 pixels

blobCounter.MinHeight = blobCounter.MinWidth = 30; blobCounter.ProcessImage(temp); total = blobCounter.GetObjectsInformation().Length; //Get total number rank = (Rank)total; //Convert it to Rank return rank; }

Untuk kartu wajah kita menggunakan template maching untuk mengenali urutan pada citra kartu
private Rank ScanFaceRank(Bitmap bmp) { Bitmap j, k, q; //Face Card Character Templates //Load Templates From Resources , j = PlayingCardRecognition.Properties.Resources.J; k = PlayingCardRecognition.Properties.Resources.K; q = PlayingCardRecognition.Properties.Resources.Q;

//Initialize template matching class with 0.75 threshold ExhaustiveTemplateMatching templateMatchin = new ExhaustiveTemplateMatching(0.75f); Rank rank = Rank.NOT_RECOGNIZED; if (templateMatchin.ProcessImage(bmp, j).Length > 0) //If Jack matches rank = Rank.Jack; if (templateMatchin.ProcessImage(bmp, k).Length > 0)//If King matches rank = Rank.King; if (templateMatchin.ProcessImage(bmp, q).Length > 0)//If Queen matches rank = Rank.Queen; return rank; }

Anda mungkin juga menyukai