2010-07-13 109 views
38

Tôi đang tìm một cách để tìm màu sắc/tông màu chủ đạo nhất trong một hình ảnh bằng cách sử dụng python. Hoặc là bóng trung bình hoặc phổ biến nhất của RGB sẽ làm. Tôi đã nhìn vào thư viện hình ảnh Python, và không thể tìm thấy bất cứ điều gì liên quan đến những gì tôi đang tìm kiếm trong hướng dẫn của họ, và cũng một thời gian ngắn tại VTK.Python - Tìm màu chủ đạo/phổ biến nhất trong một hình ảnh

Tuy nhiên, tôi đã tìm thấy tập lệnh PHP thực hiện những gì tôi cần, here (yêu cầu đăng nhập để tải xuống). Kịch bản dường như thay đổi kích thước hình ảnh thành 150 * 150, để đưa ra các màu chủ đạo. Tuy nhiên, sau đó, tôi khá lạc lối. Tôi đã xem xét việc viết một cái gì đó có thể thay đổi kích cỡ hình ảnh thành kích thước nhỏ, sau đó kiểm tra mọi pixel khác hoặc hình ảnh của nó, mặc dù tôi tưởng tượng điều này sẽ rất kém hiệu quả (mặc dù triển khai ý tưởng này như một mô-đun python C có thể là một ý tưởng).

Tuy nhiên, sau khi tất cả điều đó, tôi vẫn còn bối rối. Vì vậy, tôi quay sang bạn, SO. Có cách dễ dàng, hiệu quả để tìm màu chủ đạo trong hình ảnh không.

+0

tôi m đoán nó thay đổi kích thước hình ảnh để cho thuật toán rescaling làm một số trung bình cho bạn. – Skurmedel

Trả lời

39

Đây là mã sử dụng số PILScipy's cluster package.

Để đơn giản, tôi đã mã hóa tên tệp là "image.jpg". Thay đổi kích thước hình ảnh là dành cho tốc độ: nếu bạn không bận tâm chờ đợi, hãy nhận xét cuộc gọi thay đổi kích thước. Khi chạy trên sample image of blue peppers, nó thường cho biết màu chủ đạo là # d8c865, tương ứng với vùng màu vàng sáng ở phía dưới bên trái của hai ớt. Tôi nói "thường" bởi vì các clustering algorithm được sử dụng có một mức độ ngẫu nhiên với nó. Có nhiều cách khác nhau để bạn có thể thay đổi điều này, nhưng với mục đích của bạn, nó có thể phù hợp. (Kiểm tra các tùy chọn trên kmeans2() biến thể nếu bạn cần kết quả xác định.)

import struct 
import Image 
import numpy as np 
import scipy 
import scipy.misc 
import scipy.cluster 

NUM_CLUSTERS = 5 

print 'reading image' 
im = Image.open('image.jpg') 
im = im.resize((150, 150))  # optional, to reduce time 
ar = np.asarray(im) 
shape = ar.shape 
ar = ar.reshape(scipy.product(shape[:2]), shape[2]).astype(float) 

print 'finding clusters' 
codes, dist = scipy.cluster.vq.kmeans(ar, NUM_CLUSTERS) 
print 'cluster centres:\n', codes 

vecs, dist = scipy.cluster.vq.vq(ar, codes)   # assign codes 
counts, bins = scipy.histogram(vecs, len(codes)) # count occurrences 

index_max = scipy.argmax(counts)     # find most frequent 
peak = codes[index_max] 
colour = ''.join(chr(int(c)) for c in peak).encode('hex') 
print 'most frequent is %s (#%s)' % (peak, colour) 

Lưu ý: khi tôi mở rộng số lượng các cụm để tìm 5-10 hoặc 15, nó thường xuyên đưa ra kết quả mà là xanh hoặc hơi xanh. Với hình ảnh đầu vào, đó là những kết quả hợp lý ... Tôi không thể nói màu nào thực sự chiếm ưu thế trong hình ảnh đó, vì vậy tôi không hiểu sai thuật toán!

Cũng là một tiền thưởng nhỏ: lưu hình ảnh thu nhỏ theo chỉ với N màu sắc nhất thường xuyên:

# bonus: save image using only the N most common colours 
c = ar.copy() 
for i, code in enumerate(codes): 
    c[scipy.r_[scipy.where(vecs==i)],:] = code 
scipy.misc.imsave('clusters.png', c.reshape(*shape)) 
print 'saved clustered image' 
+1

Wow. Thật tuyệt. Hầu như chính xác những gì tôi đang tìm kiếm. Tôi đã nhìn vào scipy, và có một cảm giác câu trả lời là một nơi nào đó trong đó: P Cảm ơn bạn đã trả lời của bạn. –

+0

Câu trả lời hay. Đã làm cho tôi. Tuy nhiên, tôi có một câu hỏi nhỏ. Làm cách nào để truy cập vào màu thường xuyên thứ hai trong trường hợp màu đen là thường xuyên nhất và tôi muốn bỏ qua nó? – frakman1

+0

@ frakman1, argmax() chỉ là một chức năng tiện lợi cho phép đầu tiên. Những gì bạn cần làm là sắp xếp mảng đếm (theo dõi các chỉ mục gốc), sau đó chọn mục nhập thứ hai (hoặc thứ hai cuối cùng) thay vì mục nhập đầu tiên (điều này có hiệu quả là những gì argmax làm). –

10

Python Imaging Library có phương pháp getcolors trên các đối tượng hình ảnh:

im.getcolors() => a list of (count, color) tuples or None

Tôi đoán bạn vẫn có thể thử thay đổi kích thước hình ảnh trước đó và xem nếu nó thực hiện bất kỳ tốt hơn.

5

Bạn có thể sử dụng PIL để liên tục đổi kích thước hình ảnh xuống theo hệ số 2 trong mỗi thứ nguyên cho đến khi đạt đến 1x1. Tôi không biết thuật toán PIL sử dụng cho downscaling bởi các yếu tố lớn, do đó, đi trực tiếp đến 1x1 trong một thay đổi kích thước duy nhất có thể mất thông tin. Nó có thể không hiệu quả nhất, nhưng nó sẽ cho bạn màu "trung bình" của hình ảnh.

3

Để thêm vào câu trả lời Phêrô, nếu PIL được đem lại cho bạn một hình ảnh với chế độ "P" hoặc khá nhiều chế độ không phải là "RGBA", sau đó bạn cần áp dụng mặt nạ alpha để chuyển đổi nó thành RGBA.Bạn có thể làm điều đó khá dễ dàng với:

if im.mode == 'P': 
    im.putalpha(0) 
1

Below is a c++ Qt based example to guess the predominant image color. You can use PyQt and translate the same to Python equivalent.

#include <Qt/QtGui> 
#include <Qt/QtCore> 
#include <QtGui/QApplication> 

int main(int argc, char** argv) 
{ 
    QApplication app(argc, argv); 
    QPixmap pixmap("logo.png"); 
    QImage image = pixmap.toImage(); 
    QRgb col; 
    QMap<QRgb,int> rgbcount; 
    QRgb greatest = 0; 

    int width = pixmap.width(); 
    int height = pixmap.height(); 

    int count = 0; 
    for (int i = 0; i < width; ++i) 
    { 
     for (int j = 0; j < height; ++j) 
     { 
      col = image.pixel(i, j); 
      if (rgbcount.contains(col)) { 
       rgbcount[col] = rgbcount[col] + 1; 
      } 
      else { 
       rgbcount[col] = 1; 
      } 

      if (rgbcount[col] > count) { 
       greatest = col; 
       count = rgbcount[col]; 
      } 

     } 
    } 
    qDebug() << count << greatest; 
    return app.exec(); 
} 
2

Nếu bạn vẫn đang tìm kiếm một câu trả lời, đây là những gì làm việc cho tôi, mặc dù không phải là terribly hiệu quả:

from PIL import Image 

def compute_average_image_color(img): 
    width, height = img.size 

    r_total = 0 
    g_total = 0 
    b_total = 0 

    count = 0 
    for x in range(0, width): 
     for y in range(0, height): 
      r, g, b = img.getpixel((x,y)) 
      r_total += r 
      g_total += g 
      b_total += b 
      count += 1 

    return (r_total/count, g_total/count, b_total/count) 

img = Image.open('image.png') 
#img = img.resize((50,50)) # Small optimization 
average_color = compute_average_image_color(img) 
print(average_color) 
+0

Đối với png, bạn cần tinh chỉnh điều này một chút để xử lý thực tế là img.getpixel trả về r, g, b, a (bốn giá trị thay vì ba giá trị). Hoặc nó đã làm cho tôi anyway. – rossdavidh

+0

Điều này cân nặng pixel không đều. Pixel cuối cùng chạm vào đóng góp một nửa tổng giá trị. Pixel trước khi đóng góp một nửa. Chỉ 8 pixel cuối cùng sẽ ảnh hưởng đến mức trung bình, trên thực tế. –

+0

Bạn đúng - sai lầm ngớ ngẩn. Chỉ cần chỉnh sửa câu trả lời - hãy cho tôi biết nếu nó hoạt động. –

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