Reyza

tenang, senang, sederhana
See also: Other Geeks@INDC

Public

October 2010 - Posts

VS – Osteoporosis Detection with {ASP.NET 4.0 + AForge + Web Crop Image Control}

VS pada judul di atas artinya VERY SIMPLE (baca : sangat sederhana) :)

Dimisalkan dimiliki gambar hasil foto x-ray di bawah ini. Maka seorang dokter akan dapat menentukan gambar tulang mana yang terkena osteoporosis dan mana yang tidak. Cara penentuan oleh dokter tersebut masih dilakukan dengan melihat dan memperhatikan pola yang terlihat pada gambar.

image

Pola-pola tersebut dapat dilihat pada gambar di bawah ini.

image

Grade yang disebutkan pada gambar lebih dikenal dengan istilah Index Singh yang sudah diketahui (nilai index singh nilai pada lingkaran merah, http://id.wikipedia.org/wiki/Indeks_Singh).

  • Grade VII : Semua komponen trabekulasi terlihat mengaburkan kelompok trabekula utama
  • Grade VI : Sama dengan VII, tetapi trabekula pada segitiga Ward’s berkurang dibanding area sekitarnya dan pola trabekula utama tampak lebih jelas
  • Grade V : Pengurangan banyaknya trabekula didalam segitiga Ward’s
  • Grade IV : Penipisan korteks femur dan pengurangan dari trabekula–trabekula kompressif sekunder
  • Grade III : Ketiadaan trabekula tensil sekunder dan gangguan awal pada trabekula tensil utama
  • Grade II : Kerusakan lanjut trabekula tensil utama
  • Grade I : Penipisan trabekula kompressif utama

Indeks Singh V, VI ,VII menandakan normal, sedangkan Indeks Singh I, II, III, IV menandakan osteoporosis.

Dari gambar di atas maka dpt dilihat, tulang yg paling banyak “goresan”-nya adalah tulang yg normal, sedangkan tulang yang “mulus” adalah tulang ber-osteoporosis. Dokter saat ini sering kali menentukan hal tersebut dengan mengira-ngira nilai Indeks Singh dengan melihat “goresan-goresan” tersebut.

Bagaimana jika “mengira-ngira” ala dokter tadi digantikan dg “mengira-ngira” ala komputer? Apa yang harus dilakukan? Secara sederhana ada beberapa hal yang dapat dilakukan :

  • mengunggah foto x-ray ke sistem.
  • menampilkan foto yang telah diunggah.
  • menentukan area pada tulang untuk dideteksi.
  • meningkatkan kualitas citra area tulang yang telah dipilih.
  • mem-filter untuk tujuan deteksi, ada 4 hal yang bisa dilakukan salah satunya adalah dg filter Gabor, tetapi diblog ini hanya akan dicontohkan .
  • “menghitung” hasil deteksi dan membandingkan dengan “data pengetahuan” yang telah dimiliki untuk menampilkan nilai index singh.

Di-posting ini tidak akan menulis hal-hal yang memusingkan kepala, cukup hal-hal mudah yang diharapkan dapat menunjukkan bagaimana ide dan cara kerja sistem sederhana ini.

{Unggah Foto X-Ray & Resize}

image

Karena kebetulan menggunakan UpdatePanel-nya ASP.NET AJAX Extension maka sudah dipastikan control FileUpload tidak akan tidak akan berjalan dengan benar, karena untuk melakukan upload harus dilakukan secara “full-postback”, tidak bisa secara “async-postback”.  Oleh karena itu sering kali diakalin dg “menyimpan” control FileUpload didalam tag iframe. Tapi sukurlah sudah ada AsyncFileUpload pada ASP.NET AJAX Control Toolkit, Cukup dengan menulis seperti berikut di bawah maka “async” upload sudah bisa dinikmati dengan mudah.

Sedangkan untuk menyimpan file cukup dengan cara seperti berikut ini.

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Upload Foto</title>
<meta http-equiv="X-UA-Compatible" content="IE=7" />
<script type="text/javascript">
   1:  
   2:         function uploadComplete(sender, args) {
   3:             var namaFile = args.get_fileName();
   4:             var sizeFile = args.get_length();
   5:             alert(namaFile + " (" + sizeFile + " bytes) berhasil di upload.");
   6:         }
   7:  
   8:         function uploadError(sender, args) {
   9:             var namaFile = args.get_fileName();
  10:             alert(namaFile + " gagal di upload.");
  11:         }
  12:     
</script>
</head>
<body style="background-color:#edf5ff;background-image:url(images/white.jpg)" >
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />
<div>
<asp:UpdatePanel ID="UpdatePanel_Upload" runat="server">
<ContentTemplate>
<ajaxToolkit:AsyncFileUpload runat="server" ID="AsyncFileUpload_XRayFoto" OnClientUploadError="uploadError" OnClientUploadComplete="uploadComplete" Width="280px" UploaderStyle="Traditional" UploadingBackColor="#CCFFFF" ThrobberID="myThrobber" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>

   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   3:     AsyncFileUpload_XRayFoto.UploadedComplete += new EventHandler<AsyncFileUploadEventArgs>(AsyncFileUpload_XRayFoto_UploadedComplete);
   4:     AsyncFileUpload_XRayFoto.UploadedFileError += new EventHandler<AsyncFileUploadEventArgs>(AsyncFileUpload_XRayFoto_UploadedFileError);
   5: }
   6:  
   7: void AsyncFileUpload_XRayFoto_UploadedComplete(object sender, AsyncFileUploadEventArgs e)
   8: {
   9:     string savePath = MapPath(ConfigurationManager.AppSettings["TempUploadDir"].ToString() + "\\" + Path.GetFileName(e.filename));
  10:  
  11:     AsyncFileUpload_XRayFoto.SaveAs(savePath);
  12: }
  13:  
  14: void AsyncFileUpload_XRayFoto_UploadedFileError(object sender, AsyncFileUploadEventArgs e)
  15: {
  16: }

Dimana savePath adalah folder tempat foto akan disimpan. Pada contoh di atas, folder untuk menyimpan file ditulis pada file web.config seperti berikut.

<add key="TempUploadDir" value="~/Files/Temp/" />

Dari contoh di atas apabila file berhasil atau gagal diupload akan ditampilkan pesan dalam dialog box.

Tetapi control AsyncFileUpload tidak begitu saja normal digunakan bila halaman ASPX “berada di dalam” sebuah Master Page. Hal ini mungkin terjadi karena id tag setelah dirender berubah menyesuaikan dg control ContentPlaceHolder tempat control AsyncFileUpload berada. Dan sayangnya control AsyncFileUpload tidak bisa menggunakan ClientIDMode="Static" (hanya ada di ASP.NET 4.0) agar id tag tidak berubah saat dirender. Karena hal itu, sekali lagi terpaksa control ini dimasukkan ke dalam iframe :)

<iframe src="../Shared/Controls/UploadControl.aspx" frameborder="0" width="295px" height="32px" ></iframe>

Dimana file UploadControl.aspx seperti file ASPX di atas. Karena ada kebutuhan untuk resize foto agar ukuran width-height-nya tidak besar-besar amat dan keperlukan untuk mengubah setiap image yang diupload bertipe JPEG, maka ada sedikit tambahan method untuk keperluan itu.

Pada method AsyncFileUpload_XRayFoto_UploadedComplete diubah seperti berikut :

   1: void AsyncFileUpload_XRayFoto_UploadedComplete(object sender, AsyncFileUploadEventArgs e)
   2: {
   3:     string savePath = MapPath(ConfigurationManager.AppSettings["TempUploadDir"].ToString() + "\\" + Path.GetFileName(e.filename));
   4:     string resizeSavePath = MapPath(ConfigurationManager.AppSettings["TempUploadDir"].ToString() + "\\" + DateTime.Now.Millisecond + "" + Path.GetFileName(e.filename) + ".jpg");
   5:  
   6:     AsyncFileUpload_XRayFoto.SaveAs(savePath);
   7:  
   8:     ResizeFromStream(savePath, resizeSavePath, 730);
   9: }

Ceritanya file upload akan disimpan dg nama sesuai “savePath”, kemudian file tersebut akan di-resize dg method ResizeFromStream dengan ukuran terpanjang 730px dan disimpan sesuai dengan resizeSavePath. Dan berikut isi method ResizeFromStream (maaf nama methodnya tidak sesuai :p)

   1: using System;
   2: using AjaxControlToolkit;
   3: using System.IO;
   4: using System.Configuration;
   5: using System.Drawing;

Dan…

   1: public void ResizeFromStream(string ImageSavePath, string NewImageSavePath, int MaxSideSize)
   2: {
   3:     int intNewWidth;
   4:     int intNewHeight;
   5:  
   6:     System.Drawing.Bitmap imgInput = (Bitmap)Bitmap.FromFile(ImageSavePath);
   7:  
   8:     //get image original width and height
   9:     int intOldWidth = imgInput.Width;
  10:     int intOldHeight = imgInput.Height;
  11:  
  12:     //determine if landscape or portrait
  13:     int intMaxSide;
  14:  
  15:     if (intOldWidth >= intOldHeight)
  16:     {
  17:         intMaxSide = intOldWidth;
  18:     }
  19:     else
  20:     {
  21:         intMaxSide = intOldHeight;
  22:     }
  23:  
  24:  
  25:     if (intMaxSide > MaxSideSize)
  26:     {
  27:         //set new width and height
  28:         double dblCoef = MaxSideSize / (double)intMaxSide;
  29:         intNewWidth = Convert.ToInt32(dblCoef * intOldWidth);
  30:         intNewHeight = Convert.ToInt32(dblCoef * intOldHeight);
  31:     }
  32:     else
  33:     {
  34:         intNewWidth = intOldWidth;
  35:         intNewHeight = intOldHeight;
  36:     }
  37:  
  38:     //create new bitmap
  39:     AForge.Imaging.Filters.ResizeBilinear filter = new AForge.Imaging.Filters.ResizeBilinear(intNewWidth, intNewHeight);
  40:     System.Drawing.Bitmap newImage = filter.Apply(imgInput);
  41:  
  42:     //release used resources
  43:     imgInput.Dispose();
  44:     newImage.Save(NewImageSavePath,System.Drawing.Imaging.ImageFormat.Jpeg);
  45:     newImage.Dispose();
  46:  
  47:     Session["TempFileUrl"] = NewImageSavePath;
  48: }

Jangan lupa untuk menambahkan reference AForge pada project agar bisa menggunakan seperti pada baris ke-39 untuk keperluan resize gambar agar hasilnya lebih OK.  Sedangkan untuk menyimpan gambar dalam format JPEG dapat dilihat pada baris ke-44.

image

Untuk mendapatkan library ini bisa mengunjungi http://www.aforgenet.com/.

Kemudian url gambar akan disimpan sementara pada session, seperti yang terlihat pada baris ke-47, dimana nantinya url tersebut akan dipindahkan ke folder yang lebih mapan setelah user menyimpan data dengan mengklik tombol Save seperti pada gambar di atas.

{Menampilkan Foto}

Setelah data disimpan, maka foto dapat ditampilkan … pasti yg membaca posting ini sudah tahu bagaimana menampilkan image, jadi tidak perlu di bahas.

{Menentukan Area Tulang untuk Dideteksi}

image

image

Prilakunya mirip dengan fitur Add Note yg ada di Flickr.

image

Gambar pada foto X-Ray yang ditampilkan (pada tab Foto XRay), kemudian dipilih area yang diinginkan dengan kotak dinamis yang bisa dipindahkan lokasinya dan diubah ukuran panjang-tinggi-nya. Setelah tombol Crop diklik maka area tersebut akan dipotong dan ditampilkan pada tab Foto Crop. Begitu kira-kira yang harus dibuat.

Untuk membuat hal di atas terjadi maka hal yang dilakukan tidak sulit, langkah pertama cukup mengunduh control Web Crop Image dalam bentuk fisik CS.Web.UI.CropImage.dll di  http://webcropimage.codeplex.com/. Setelah menambahkan DLL tersebut sebagai reference dalam project dan menambahkan baris ke-4 berikut ini pada web.config.

   1: <pages theme="ODSS">
   2:   <controls>
   3:     <add tagPrefix="ajaxToolkit" namespace="AjaxControlToolkit" assembly="AjaxControlToolkit"/>
   4:     <add tagPrefix="cs" namespace="CS.Web.UI" assembly="CS.Web.UI.CropImage"/>
   5:   </controls>
   6: </pages>

Kemudian apabila gambar foto X-Ray ditampilkan dengan menggunakan control Image dengan ID=Image_XRay, maka cukup tambahkah control Web Crop Image seperti dibawah ini.

<asp:Image ID="Image_XRay" runat="server" />
<cs:CropImage ID="CropImage_XRay" X="13" X2="113" Y="13" Y2="113" runat="server" Image="Image_XRay" />

Maka akan dapat dilihat gambar seperti ini.

image

Selanjutnya bila ingin meng-crop area tersebut dan menyimpannya sebagai file, maka cukup tuliskan baris berikut.

using CS.Web.UI;
....
string folder = ConfigurationManager.AppSettings["CropedImagesDir"].ToString();
string fileName = folder + "/" + DateTime.Now.Year.ToString() + "" + DateTime.Now.Month.ToString() + "" + DateTime.Now.Day.ToString() + "_" + DateTime.Now.Hour.ToString() + "" + DateTime.Now.Minute.ToString() + "" + DateTime.Now.Second.ToString() + "" + DateTime.Now.Millisecond.ToString() + ".jpg";
CropImage_XRay.Crop(Server.MapPath(fileName));

Pada baris terakhir dapat dilihat bagaimana proses crop terjadi.

{Meningkatkan Kualitas Citra}

Setelah area di-crop maka tinggal dilakukan pengolahan citra. Sekali lagi digunakan AForge (http://www.aforgenet.com/) untuk proses ini. Setelah area crop disimpan maka “simpan data” gambar tersebut dalam suatu variable agar mudah diolah, seperti berikut :

System.Drawing.Bitmap image = (Bitmap)Bitmap.FromFile(Server.MapPath(fileName));

Sedangkah untuk meningkatkan qualitas citra dapat digunakan filter Media (untuk menekan pengaruh gangguan noise pada gambar) dan contrast stretching (sebagai proses standardisasi (dinamika) intensitas citra). Kedua filter ini sudah tersedia di dalam AForge. Karena AForge mengijinkan untuk melakukan rangkaian filter maka dapat ditulis kode seperti berikut.

   1: AForge.Imaging.Image.FormatImage(ref image);
   2: AForge.Imaging.Filters.FiltersSequence filter = new AForge.Imaging.Filters.FiltersSequence();
   3: filter.Add(new AForge.Imaging.Filters.Grayscale(0.2125, 0.7154, 0.0721));
   4: filter.Add(new AForge.Imaging.Filters.Median());
   5: filter.Add(new AForge.Imaging.Filters.ContrastStretch());
   6:  
   7: System.Drawing.Bitmap newImage = filter.Apply(image);

Pada baris ke-3 adalah memulai untuk mendaftarkan filter yang digunakan, dimulai dengan filter Gayscale, untuk memastikan gambar “berwarna hitam-putih”, setelah itu pada baris ke-4 mulai mendaftarkan filter median dan baris ke-5 mendaftarkan filter contrast stretch.  Terakhir adalah baris ke-7, yaitu “mengeksekusi” filter-filter yang telah didaftarkan terhadap gambar (image), dan menyimpan hasilnya pada newImage.

Apabila ingin menyimpan hasilnya cukup dilakukan hal berikut :

   1: string binerImageFileName = folder + "/" + DateTime.Now.Year.ToString() + "" + DateTime.Now.Month.ToString() + "" + DateTime.Now.Day.ToString() + "_" + DateTime.Now.Hour.ToString() + "" + DateTime.Now.Minute.ToString() + "" + DateTime.Now.Second.ToString() + "" + DateTime.Now.Millisecond.ToString() + "_" + rnd.Next() + ".jpg";
   2: image.Dispose();
   3: newImage.Save(Server.MapPath(binerImageFileName));
   4: newImage.Dispose();

Untuk menampilkannya ke halaman web pasti sudah pada bisa, jadi tidak perlu repot-repot ditulis caranya diposting ini :)

{Menentukan Index Singh}

Untuk menentukan Index Singh sebenarnya banyak yang harus dilakukan, tetapi di posting ini akan dibuat sederhana untuk menunjukkan ide saja. Misalnya yang dicari adalah banyaknya “goresan” pada area tulang, maka yang paling sederhana adalah mengubah gambar menjadi gambar biner (cuma hitam dan putih) dan edge detectoruntuk menemukan garis-garis pada area, kemudian tinggal menghitung prosentase warna putih terhadap warna hitam.

Untuk mengubah gambar menjadi biner dan edge detector cukup menambahkan filter ini :

   1: filter.Add(new AForge.Imaging.Filters.SISThreshold());
   2: filter.Add(new AForge.Imaging.Filters.CannyEdgeDetector());

Sedangkan untuk menghitung jumlah pixel putih dan hitam dapat dilakukan sebagai berikut :

   1: image = (Bitmap)Bitmap.FromFile(Server.MapPath(binerImageFileName));
   2: ImageStatistics stat = new ImageStatistics(image);
   3: int whitePixel = stat.PixelsCountWithoutBlack;
   4: int blackPixel = stat.PixelsCount - whitePixel;

Dengan membandingkan dengan data “pengetahuan” yang sudah ada, maka nanti akan didapat nilai index singh.

image

Kira-kira begitulah langkah-langkah yang dapat dilakukan untuk menentukan tulang ber-osteoporosis atau tidak dengan memanfaatkan ASP.NET, AForge dan Web Crop Image. Semoga memberi pencerahan.

 

Selamat menikmati akhir week end.

Share this post: | | | |
Posted: Oct 17 2010, 05:19 PM by reyza | with 5 comment(s) |
Filed under:
Web Site vs Web Application : Mengelola User Profile

Katanya, makin banyak waktu yang dihabiskan untuk menulis program, makin banyak juga masalah yang akan ditemui. Makin banyak masalah yang ditemui makin banyak pula solusi untuk memecahkan masalah tersebut.

Sebelumnya, aplikasi web yang dibuat lebih sering menggunakan Web Site Project Template dibandingkan dengan Web Application Template. Kenapa? Karena sebagai orang pemalas, sangat merepotkan kalau harus melakukan build setiap ada perubahan pada code behind bila menggunakan Web Application. Tetapi karena kemalasan ini juga, terpaksa harus menggunakan Web Application, karena akan secara otomatis sudah secara default dibuatkan “sesuatu” untuk menghubungkan Silverlight Application dan WCF Ria Service .

image

image

Oleh karena perlu gabungan ASP.NET dengan Silverlight, maka dipilihlah kolaborasi antara ASP.NET Web Application dengan Silverlight, agar bisa “Enable WCF Ria Service” secara default.

Untuk proses authentication, akan lebih mudah menggunakan fitur yang telah disediakan oleh ASP.NET (Membership, Role .. etc).  Semisal ingin dibuat user pada sistem dengan atribut-atribut seperti yang terlihat pada form di bawah ini. Maka dapat dimanfaatkan “fitur” Profile yang dapat dimanfaatkan untuk menambah atribut-atribut baru seperti Dokter ID, Nama Dokter, Telepon dan Alamat.

image

Pada Web Site maka cukup ditambahkan atribut-atribut tersebut pada web.config seperti berikut ini.

<profile>
  <providers>
    <clear/>
    <add name="AspNetSqlProfileProvider" connectionStringName="ODSSUserConnString" applicationName="Caltris" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
  </providers>
  <properties>
    <add name="IdDokter" type="System.String" serializeAs="String" allowAnonymous="false" defaultValue="" readOnly="false"/>
    <add name="NamaDokter" type="System.String" serializeAs="String" allowAnonymous="false" defaultValue="" readOnly="false"/>
    <add name="Teleon" type="System.String" serializeAs="String" allowAnonymous="false" defaultValue="" readOnly="false"/>
    <add name="Alamat" type="System.String" serializeAs="String" allowAnonymous="false" defaultValue="" readOnly="false"/>
  </properties>
</profile>

Maka secara otomatis akan dibuatkan class ProfileCommon dengan isi sebagai berikut. (kode di atas mestinya tertulis Telepon, tapi karena males memperbaiki maka masih tertulis Teleon.

using System;
using System.Web.Profile;
 
public class ProfileCommon : ProfileBase
{
    public ProfileCommon();
 
    public virtual string IdDokter { get; set; }
    public virtual string NamaDokter { get; set; }
    public virtual string Telepon { get; set; }
    public virtual string Alamat { get; set; }
 
    public virtual ProfileCommon GetProfile(string username);
}

Tetapi hal otomatisasi hal di atas ternyata tidak terjadi pada Web Application (keadaan : ASP.NET 4.0 dan VS 2010, untuk versi yang berbeda mungkin terjadi hal yang berbeda pula). Class ProfileCommon tidak dibuat secara otomatis.

Maka pada project Web Application tinggal dibuat saja satu class, semisal namanya adalah UserProfile dengan letak terserah keinginan (berbeda dengan Web Site yang mengharuskan meletakkan class di dalam folder App_Code).  Berikut adalah isi dari class UserProfile.

   1: using System.Web.Profile;
   2: using System.Web.Security;
   3:  
   4:  
   5: namespace ODSS.Security
   6: {
   7:     public class UserProfile : ProfileBase
   8:     {
   9:         public static UserProfile GetUserProfile(string username)
  10:         {
  11:             return Create(username) as UserProfile;
  12:         }
  13:  
  14:         public static UserProfile GetUserProfile()
  15:         {
  16:             return Create(Membership.GetUser().UserName) as UserProfile;
  17:         }
  18:  
  19:         [SettingsAllowAnonymous(false)]
  20:         public string Fullname
  21:         {
  22:             get { return base["Fullname"] as string; }
  23:             set { base["Fullname"] = value; }
  24:         }
  25:  
  26:         [SettingsAllowAnonymous(false)]
  27:         public string Address
  28:         {
  29:             get { return base["Address"] as string; }
  30:             set { base["Address"] = value; }
  31:         }
  32:  
  33:         [SettingsAllowAnonymous(false)]
  34:         public string Phone
  35:         {
  36:             get { return base["Phone"] as string; }
  37:             set { base["Phone"] = value; }
  38:         }
  39:  
  40:         [SettingsAllowAnonymous(false)]
  41:         public string DocterId
  42:         {
  43:             get { return base["DocterId"] as string; }
  44:             set { base["DocterId"] = value; }
  45:         }
  46:  
  47:     }
  48: }

Untuk membuat class ini dibutuhkan 2 namespace yang dapat dilihat pada baris ke-1 dan ke-2.  Setelah class tersebut dibuat maka untuk mempersingkat penulisan pada web.config dapat dibuat seperti berikut ini.

   1: <profile inherits="ODSS.Security.UserProfile">
   2:   <providers>
   3:     <clear/>
   4:     <add name="AspNetSqlProfileProvider" connectionStringName="ODSSUserConnString" applicationName="ODSS" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
   5:   </providers>
   6: </profile>

Disini dapat dilihat tidak perlu lagi dituliskan atribut-atribut untuk profile user.  Cukup dituliskan seperti pada baris ke-1, dimana ODSS.Security.UserProfile adalah class lengkap dengan namespace-nya.

Selanjutnya sudah tidak ada lagi perbedaan antara Web Site dan Web Application (semoga)….kecuali jangan lupa untuk melakukan “build” :)

#catatanbagiorangpelupasepertigw

Share this post: | | | |
Posted: Oct 09 2010, 03:29 AM by reyza | with no comments
Filed under: