Thẻ

, , ,

Bài toán:

Cho một ảnh đã được cân bằng mức xám, giờ ta muốn thực hiện lại phép biến đổi đó lên một ảnh khác! Công việc này gọi là Histogram Matching, tức là thực hiện cân bằng mức xám của ảnh khác lên ảnh của mình!

Adjusted Image (Ảnh đã được Histogram Equalization)Equalized

Ảnh muốn sử dụng hiệu ứng mà ảnh trên đã làm:
namlunoy_histogram_matching_3       Kết quả:  namlunoy_histogram_matching_4

Để thực hiện Histogram Matching ta cần thực hiện qua 3 bước sau:

Ta xét ví dụ sau:
Gọi ảnh 1 là ảnh gốc chưa chỉnh sửa (source), và ảnh 2 là ảnh đã được cân bằng mức xám (model). Ảnh với mức xám là 8 như sau:

Ảnh 1 —– Ảnh 2

namlunoy_histogram_matching_1 namlunoy_histogram_matching_2

Bước 1: Tính CDF tương ứng của 2 ảnh.

Như bài trước ta đã học, ta có cdf của 2 ảnh tương ứng như sau:
Ảnh 1:

Mức xám Số lượng Xác xuất CDF
1 1 0,25 0,25
2 2 0,25 0,5
3 3 0,25 0,75
4 4 0,25 1

Ta đươc: F1[1] = 0.25; F1[2] = 0.5; …

Ảnh 2:

Mức xám Số lượng Xác xuất CDF
2 1 0,25 0,25
4 2 0,25 0,5
5 3 0,25 0,75
6 4 0,25 1

Ta được: F2[2] = 0.25; F[4] = 0.5; …

Bước 2: Tìm hàm Matching M()

Tìm gọi mức xám của 2 ảnh lần lược là g1, g2, tìm g2 của ảnh 2,
sao cho F1[g1] = F2[g2]. Từ đó ta tìm được hàm M[g1] = g2.

Theo trên ta có:
F1[1] = 0.25 = F2[2]     ===>  M[1] = 2
F1[2] = 0.5 = F2[4]     ===>  M[2] = 4
F1[3] = 0.75 = F2[5]     ===>  M[3] = 5
F1[4] = 1 = F2[6]     ===>  M[4] = 6

Bước 3: Mực hiện matching với ảnh gốc theo hàm M

Ta chuyển hết các mức xám g1 của ảnh 1 thành mức xám M[g1] tương ứng.

Source code:

unsafe
     public static Bitmap HistogramMatching(Bitmap model, Bitmap source)
     {
         Bitmap dest = (Bitmap)source.Clone();

         //Bước 1: Tính cdf của 2 ảnh
         float[] F1 = CDF(source);
         float[] F2 = CDF(model);

         //Bước 2: Tính M sao cho F1[g1] = F2[g2]
         int[] M = new int[256];
         for (int i = 0; i < 256; i++)
         {
             M[i] = i;
             for (int j = 0; j < 256; j++)
             {
                 if ((F1[i] - F2[j]) < 0.001)
                 {
                     M[i] = j;
                     break;
                 }
             }
         }

         //Bước 3: Matching theo M

         BitmapData data = dest.LockBits(new Rectangle(0, 0, dest.Width, dest.Height), ImageLockMode.ReadOnly, dest.PixelFormat);
         byte* p = (byte*)data.Scan0;
         int offset = data.Stride - dest.Width * 3;

         for (int hang = 0; hang < dest.Height; hang++)
         {
             for (int cot = 0; cot < dest.Width; cot++)
             {
                 p[0] = p[1] = p[2] = (byte)M[p[2]];
                 p += 3;
             }
             p += offset;
         }
         dest.UnlockBits(data);
         return dest;
     }

     /// <summary>
     /// Tính CDF của 1 ảnh
     /// </summary>
     unsafe
     public static float[] CDF(Bitmap bitmap)
     {
         float[] cdf = new float[256];
         BitmapData data = bitmap.LockBits(new Rectangle(0,0,bitmap.Width,bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
         byte* p = (byte*)data.Scan0;
         int offset = data.Stride - bitmap.Width * 3;

         //Bước 1: Tình tần số
         int total = bitmap.Width * bitmap.Height;
         float[] xacsuat = new float[256];
         int[] count = new int[256];

         for (int hang = 0; hang < bitmap.Height; hang++)
         {
             for (int cot = 0; cot < bitmap.Width; cot++)
             {
                 count[p[2]]++;
                 p += 3;
             }
             p += offset;
         }
         for (int i = 0; i < 256; i++)
             xacsuat[i] = count[i] * 1.0f / total;

         //Bước 2: Tính cdf
         cdf[0] = xacsuat[0];
         for (int i = 1; i < 256; i++)
             cdf[i] = cdf[i - 1] + xacsuat[i];

         bitmap.UnlockBits(data);
         return cdf;
     }

Link project: https://www.dropbox.com/s/zefjfhom4ve3si8/HistogramMatching.rar?dl=0

Tham khảo: http://en.wikipedia.org/wiki/Histogram_matching

 

new blog: hoangvancong.com

Advertisements