2010-03-30 49 views
8

Tôi muốn lấy phần lớn màu trong ảnh nền trong .NET. Có thể không?Làm thế nào để có được một màu đa số trong một hình ảnh?

+0

Bạn có thể làm sáng tỏ ý nghĩa của 'màu đa'? Bạn có nghĩa là màu sắc thường được sử dụng nhất trong một hình ảnh; màu trung bình của hình ảnh; màu RGB có giá trị 'cao nhất'? – Seidr

+3

Bạn có thể cần phải làm rõ những gì bạn có ý nghĩa bởi "đa số màu". Đối với hầu hết các hình ảnh, không có bất kỳ một màu nào sẽ tạo thành một đa số phù hợp. Tôi cho rằng bạn có nghĩa là màu sắc thường xuyên nhất (pixel theo pixel) hoặc thành phần chi phối (hoặc là R, G hoặc B). – MusiGenesis

+0

Bạn có cần tốc độ hoặc độ chính xác không? Bạn cũng muốn màu sắc được sử dụng nhiều nhất, màu trung bình hoặc màu xu hướng? Bạn có cần RGB hỗn hợp hoặc sperated? –

Trả lời

6

Bạn có thể lặp qua tất cả các điểm ảnh trong hình ảnh và sử dụng phương thức getPixel để xác định giá trị RGB. Sau đó, bạn có thể có một từ điển lưu trữ giá trị ARGB cùng với một số đếm. Sau đó, bạn có thể xem giá trị ARGB nào hiện diện nhiều nhất trong hình ảnh.

var list = new Dictionary<int, int>(); 
Bitmap myImage = (Bitmap)Bitmap.FromFile("C:/test.jpeg"); 
for (int x = 0; x < myImage.Width; x++) 
{ 
    for (int y = 0; y < myImage.Height; y++) 
    { 
     int rgb = myImage.GetPixel(x, y).ToArgb(); 
     if (!list.ContainsKey(rgb)) 
      list.Add(rgb, 1); 
     else 
      list[rgb]++; 
    } 
} 

Như đã chỉ ra điều này không có cảm tình cho các màu tương tự. Nếu bạn muốn có một màu đa số 'chung' hơn, bạn có thể có ngưỡng tương tự. ví dụ. chứ không phải là:

if (!list.ContainsKey(rgb)) 
     list.Add(rgb, 1); 
    else 
     list[rgb]++; 

bạn có thể làm:

var added = false; 
for (int i = 0; i < 10; i++) 
{ 
    if (list.ContainsKey(rgb+i)) 
    { 
     list[rgb+i]++; 
     added = true; 
     break; 
    } 
    if (list.ContainsKey(rgb-i)) 
    { 
     list[rgb-i]++; 
     added = true; 
     break; 
    } 
} 
if(!added) 
    list.Add(rgb, 1); 

Bạn có thể lên ngưỡng từ 10 đến bất cứ điều gì bạn cần.

+0

Nếu đây là một "màu sắc thực" jpeg ... có thể là tấn các giá trị trong từ điển với số lượng rất ít cho mỗi khóa. –

+0

Đồng ý. Bạn sẽ cần một số loại binning hoặc nhóm để làm việc decently. Với cái này bạn có thể có 2 điểm ảnh hoàn toàn màu đỏ và một tấn các biến thể tinh tế của màu đen và xuất hiện với màu đỏ là màu chủ đạo. – RandomEngy

+0

Về mặt kỹ thuật, màu đỏ sẽ là màu đa số. – CeejeeB

3

Giả sử bạn mô tả màu của từng pixel bằng cách sử dụng RGB (so với, giả sử, CMYK), bạn có thể tạo một mảng 3D (một thứ nguyên cho mỗi R, G và B). Sau đó quyết định số thùng bạn muốn có trong mỗi kích thước - càng nhiều thùng, thì sự khác biệt lớn hơn mà bạn đang tạo ra giữa các màu sắc tương tự.

Khi đã xong, chỉ cần lặp qua biểu diễn bitmap của hình ảnh của bạn tổng hợp # pixel rơi vào mỗi ô trong mảng 3d của bạn. Ô có tổng cao nhất sẽ là màu chủ đạo.

Bạn có thể muốn làm cho thuật toán của mình dễ dàng định cấu hình cho # thùng trong mỗi thứ nguyên sao cho bạn có thể điều chỉnh mức độ phân biệt giữa các màu tương tự nhau.

image = new Bitmap("C:\\test.bmp", true); 
int x, y; 
// Loop through the images pixels to product 3d histogram 
for(x=0; x<image.Width; x++) 
{ 
    for(y=0; y<image.Height; y++) 
    { 
     Color pixelColor = image.GetPixel(x, y); 

     // Increment color count in appropriate cell of your 3d histogram here 
     ... 
    } 
} 
5

Điều này sẽ trả lại màu trung bình cho hình ảnh.

static Color AverageColor(string fileName) 
{ 
    using (var bmp = new Bitmap(fileName)) 
    { 
     int width = bmp.Width; 
     int height = bmp.Height; 
     int red = 0; 
     int green = 0; 
     int blue = 0; 
     int alpha = 0; 
     for (int x = 0; x < width; x++) 
      for (int y = 0; y < height; y++) 
      { 
       var pixel = bmp.GetPixel(x, y); 
       red += pixel.R; 
       green += pixel.G; 
       blue += pixel.B; 
       alpha += pixel.A; 
      } 

     Func<int, int> avg = c => c/(width * height); 

     red = avg(red); 
     green = avg(green); 
     blue = avg(blue); 
     alpha = avg(alpha); 

     var color = Color.FromArgb(alpha, red, green, blue); 

     return color; 
    } 
} 
+0

Tôi không nghĩ kết quả đủ điều kiện là "màu đa số" vì có thể thậm chí không có một pixel nào là màu trung bình. – Kaniu

+0

Rất đúng ... và đó là lý do tại sao tôi hỏi chi tiết hơn trong bình luận của tôi theo câu hỏi. Không có phản hồi vì vậy đây là mục nhập của tôi. –

4

Điều này sẽ trả lại màu trung bình cho hình ảnh khi sử dụng truy cập con trỏ không an toàn. Lưu ý: mã chỉ được điều chỉnh cho 24bppRgb, có thể được điều chỉnh cho các định dạng pixel khác.

unsafe static Color GetColor(string filename) 
    { 
     using (var image = (Bitmap)Bitmap.FromFile(filename)) 
     { 
      if (image.PixelFormat != PixelFormat.Format24bppRgb) throw new NotSupportedException(String.Format("Unsupported pixel format: {0}", image.PixelFormat)); 

      var pixelSize = 3; 
      var bounds = new Rectangle(0, 0, image.Width, image.Height); 
      var data = image.LockBits(bounds, ImageLockMode.ReadOnly, image.PixelFormat); 

      long r = 0; 
      long g = 0; 
      long b = 0; 

      for (int y = 0; y < data.Height; ++y) 
      { 
       byte* row = (byte*)data.Scan0 + (y * data.Stride); 
       for (int x = 0; x < data.Width; ++x) 
       { 
        var pos = x * pixelSize; 
        b += row[pos]; 
        g += row[pos + 1]; 
        r += row[pos + 2]; 
       } 
      } 

      r = r/(data.Width * data.Height); 
      g = g/(data.Width * data.Height); 
      b = b/(data.Width * data.Height); 
      image.UnlockBits(data); 

      return Color.FromArgb((int)r, (int)g, (int)b); 
     } 
    } 

ý chính: http://gist.github.com/349210

+0

+1 cho hiệu suất. Gọi GetPixel trên mỗi điểm ảnh là cực kỳ chậm. – RandomEngy

+1

Cực kỳ chậm là một chút cường điệu. Tôi đã có thể sử dụng phương pháp GetPixel để phân tích các khung hình video ở tốc độ 15fps 800x600. Vì vậy, nếu áp phích chỉ cần phân tích một hình ảnh đơn GetPixel là quá đủ. – CeejeeB

Các vấn đề liên quan