Nhận màu sắc trung bình của hình ảnh qua Javascript


118

Không chắc điều này có thể thực hiện được, nhưng đang tìm cách viết một đoạn script trả về giá trị trung bình hexhoặc rgbgiá trị cho một hình ảnh. Tôi biết nó có thể được thực hiện trong AS nhưng đang tìm cách thực hiện nó trong JavaScript.


19
bạn của tôi, Boaz đã từng đi trên con đường này trước đây (hy vọng anh ấy thích thú) nhưng trung bình thường để lại cho bạn một màu giống như chất nôn. Điều bạn thực sự muốn là "màu chủ đạo". Có một số cách để đạt được điều đó (biểu đồ màu w / bucketing động, v.v.) nhưng tôi nghĩ điều đó có lẽ chính xác hơn cho kết quả dự định của bạn.
Paul Ailen

Câu trả lời:


149

AFAIK, cách duy nhất để làm điều này là <canvas/>...

DEMO V2 : http://jsfiddle.net/xLF38/818/

Lưu ý, điều này sẽ chỉ hoạt động với các hình ảnh trên cùng một miền và trong các trình duyệt hỗ trợ canvas HTML5:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
        defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
        canvas = document.createElement('canvas'),
        context = canvas.getContext && canvas.getContext('2d'),
        data, width, height,
        i = -4,
        length,
        rgb = {r:0,g:0,b:0},
        count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return defaultRGB;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

Đối với IE, hãy kiểm tra excanvas .


1
Có cách nào để lấy biểu đồ màu cho một hình ảnh nằm trên một miền khác không?
Dan Loewenherz

1
Dan: Tôi nghĩ rằng bạn sẽ gặp phải hạn chế về nguồn gốc chéo nếu bạn cố gắng truy cập dữ liệu hình ảnh trên một hình ảnh từ một miền khác. Đó là một kẻ vô tích sự.

8
tôi nghĩ rằng có vị trí mong đợi cho giá trị màu xanh lam và xanh lục, bạn viết 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'và điều đó nên có 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. thật lạ khi màu chủ đạo là xanh dương nhưng kết quả lại là xanh lá :).
Ariona Rian

7
Đã tìm thấy một giải pháp tuyệt vời cho các hạn chế về nguồn gốc chéo: chỉ cần thêm img.crossOrigin = '';trước khi đặt srcthuộc tính. Tìm thấy trên: coderwall.com/p/pa-2uw
mhu

Không cần đếm ngược, vì count === length / 4 / blockSize;)
yckart

84

Hình Tôi muốn đăng một dự án mà tôi đã xem gần đây để lấy màu chủ đạo:

Kẻ trộm màu

Một tập lệnh để lấy màu chủ đạo hoặc bảng màu đại diện từ hình ảnh. Sử dụng javascript và canvas.

Các giải pháp khác đề cập và đề xuất màu chủ đạo không bao giờ thực sự trả lời câu hỏi trong ngữ cảnh thích hợp ("trong javascript"). Hy vọng rằng dự án này sẽ giúp những người muốn làm điều đó.


55

"Màu thống trị" là khó khăn. Những gì bạn muốn làm là so sánh khoảng cách giữa mỗi pixel và mọi pixel khác trong không gian màu (Khoảng cách Euclide), sau đó tìm pixel có màu gần nhất với mọi màu khác. Pixel đó là màu chủ đạo. Màu trung bình thường sẽ là bùn.

Tôi ước tôi có MathML ở đây để cho bạn thấy Khoảng cách Euclide. Google nó.

Tôi đã hoàn thành việc thực thi ở trên trong không gian màu RGB bằng PHP / GD tại đây: https://gist.github.com/cf23f8bddb307ad4abd8

Tuy nhiên, điều này rất tốn kém về mặt tính toán. Nó sẽ làm hỏng hệ thống của bạn trên các hình ảnh lớn và chắc chắn sẽ làm hỏng trình duyệt của bạn nếu bạn thử nó trong ứng dụng khách. Tôi đang làm việc để cấu trúc lại việc thực thi của mình để: - lưu trữ kết quả trong một bảng tra cứu để sử dụng trong tương lai trong việc lặp lại trên mỗi pixel. - để chia hình ảnh lớn thành các lưới 20px 20px để chiếm ưu thế cục bộ. - sử dụng khoảng cách euclide giữa x1y1 và x1y2 để tìm ra khoảng cách giữa x1y1 và x1y3.

Vui lòng cho tôi biết nếu bạn đạt được tiến bộ trong lĩnh vực này. Tôi sẽ rất vui khi thấy nó. Tôi sẽ làm giống.

Canvas chắc chắn là cách tốt nhất để làm điều này trong khách hàng. SVG thì không, SVG dựa trên vector. Sau khi tôi thực hiện xong, điều tiếp theo tôi muốn làm là chạy nó trong canvas (có thể với webworker để tính toán khoảng cách tổng thể của mỗi pixel).

Một điều khác cần suy nghĩ là RGB không phải là không gian màu tốt để thực hiện điều này, bởi vì khoảng cách euclid giữa các màu trong không gian RGB không gần lắm với khoảng cách thị giác. Không gian màu tốt hơn để làm điều này có thể là LUV, nhưng tôi không tìm thấy thư viện tốt cho điều này hoặc bất kỳ thuật ngữ nào để chuyển đổi RGB sang LUV.

Một cách tiếp cận hoàn toàn khác sẽ là sắp xếp các màu của bạn theo cầu vồng và xây dựng biểu đồ có dung sai để tính đến các sắc thái khác nhau của màu. Tôi chưa thử điều này, vì việc phân loại màu sắc trong cầu vồng rất khó và biểu đồ màu cũng vậy. Tôi có thể thử điều này tiếp theo. Một lần nữa, hãy cho tôi biết nếu bạn đạt được bất kỳ tiến bộ nào ở đây.


6
Đây là một trong những câu trả lời mà tôi ước gì có thể làm nhiều hơn là chỉ 1 :-)
David Johnstone

3
Câu trả lời xuất sắc mặc dù một giải pháp rõ ràng không được đưa ra. Điều này sẽ giúp tôi quyết định bước tiếp theo của tôi ...
bgreater

Đây là một câu trả lời tuyệt vời. Tôi đã viết một vài mô-đun nút để thực hiện tính toán khoảng cách euclid không phải trong LUV, mà trong không gian màu L a b *. Xem github.com/zeke/color-namergithub.com/zeke/euclidean-distance
Zeke

1
@ người dùng: Tôi chỉ tự hỏi: chẳng phải a, chẳng hạn, mẫu 1% có đủ trên hình ảnh lớn để tăng tốc độ tính toán không?
jackthehipster

Nghe giống như đường trung bình hình học. Có lẽ điều này có thể giúp đỡ? stackoverflow.com/questions/12934213/… và nếu điểm bạn đang tìm phải tồn tại trong tập hợp gốc: en.m.wikipedia.org/wiki/Medoid#Algorithm_to_compute_medoids
Lawrence Weru

16

Đầu tiên: nó có thể được thực hiện mà không cần HTML5 Canvas hoặc SVG.
Trên thực tế, ai đó vừa quản lý để tạo tệp PNG phía máy khách bằng JavaScript , không có canvas hoặc SVG, sử dụng lược đồ URI dữ liệu .

Thứ hai: bạn có thể thực sự không cần Canvas, SVG hoặc bất kỳ thứ nào ở trên.
Nếu bạn chỉ cần xử lý hình ảnh ở phía máy khách mà không cần sửa đổi chúng, thì tất cả điều này là không cần thiết.

Bạn có thể lấy địa chỉ nguồn từ thẻ img trên trang, thực hiện yêu cầu XHR cho nó - hầu hết có thể đến từ bộ nhớ cache của trình duyệt - và xử lý nó dưới dạng luồng byte từ Javascript.
Bạn sẽ cần hiểu rõ về định dạng hình ảnh. (Trình tạo ở trên một phần dựa trên nguồn libpng và có thể cung cấp một điểm khởi đầu tốt.)


+1 cho giải pháp bytestream. Đây là một lựa chọn tốt nếu lựa chọn duy nhất là tạo nó trên ứng dụng khách.
loretoparisi

11

Tôi sẽ nói thông qua thẻ canvas HTML.

Bạn có thể tìm thấy ở đây một bài đăng của @Georg nói về một đoạn mã nhỏ của nhà phát triển Opera:

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = 255 - pix[i  ]; // red
    pix[i+1] = 255 - pix[i+1]; // green
    pix[i+2] = 255 - pix[i+2]; // blue
    // i+3 is alpha (the fourth element)
}

// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);

Điều này đảo ngược hình ảnh bằng cách sử dụng giá trị R, G và B của mỗi pixel. Bạn có thể dễ dàng lưu trữ các giá trị RGB, sau đó làm tròn các mảng Đỏ, Xanh lục và Xanh lam và cuối cùng chuyển đổi chúng trở lại thành mã HEX.


bất kỳ cơ hội nào có một giải pháp thay thế IE cho giải pháp canvas?
bgreater

@ Ben4Himv: Có Bản xem trước nền tảng IE9 ;-) nhưng nghiêm túc mà nói, tôi e là không.
Andy E


4

Javascript không có quyền truy cập vào dữ liệu màu pixel riêng lẻ của một hình ảnh. Ít nhất, có lẽ không phải cho đến khi html5 ... tại thời điểm đó lý do là bạn sẽ có thể vẽ một hình ảnh vào canvas, và sau đó kiểm tra canvas (có thể, tôi chưa bao giờ tự mình làm điều đó).


2

Giải pháp tất cả trong một

Cá nhân tôi sẽ kết hợp Color Thief cùng với phiên bản sửa đổi của Tên Màu đó để có được một mảng đầy đủ các kết quả màu chủ đạo cho hình ảnh.

Thí dụ:

Hãy xem xét hình ảnh sau:

nhập mô tả hình ảnh ở đây

Bạn có thể sử dụng mã sau để trích xuất dữ liệu hình ảnh liên quan đến màu chủ đạo:

let color_thief = new ColorThief();
let sample_image = new Image();

sample_image.onload = () => {
  let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
    const hex = x.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
    
  }).join(''));
  
  console.log(result[0]); // #f0c420     : Dominant HEX/RGB value of closest match
  console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
  console.log(result[2]); // #ffff00     : Dominant HEX/RGB value of shade of closest match
  console.log(result[3]); // Yellow      : Dominant color name of shade of closest match
  console.log(result[4]); // false       : True if exact color match
};

sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;

1

Đây là về "Lượng tử hóa màu" có một số cách tiếp cận như MMCQ (Lượng tử hóa cắt trung vị đã sửa đổi) hoặc OQ (Lượng tử hóa Octree). Cách tiếp cận khác nhau sử dụng K-Means để thu được các cụm màu.

Tôi đã tổng hợp tất cả lại với nhau ở đây, vì tôi đang tìm giải pháp cho tvOSnơi có một tập hợp con của XHTML, không có <canvas/>phần tử:

Tạo các Màu ưu thế cho hình ảnh RGB với XMLHttpRequest


1

Cách nhanh nhất để có được màu trung bình của hình ảnh với dataurisự hỗ trợ:

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}

1

Như đã chỉ ra trong các câu trả lời khác, thường những gì bạn thực sự muốn có màu chủ đạo thay vì màu trung bình có xu hướng là nâu. Tôi đã viết một kịch bản có màu phổ biến nhất và đăng nó trên ý chính này


-2

Có một công cụ trực tuyến pickimagecolor.com giúp bạn tìm màu trung bình hoặc màu chủ đạo của hình ảnh. Bạn chỉ cần tải hình ảnh lên từ máy tính của mình rồi nhấp vào hình ảnh đó. Nó cho màu sắc trung bình ở HEX, RGB và HSV. Nó cũng tìm các sắc thái màu phù hợp với màu đó để lựa chọn. Tôi đã sử dụng nó nhiều lần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.