รับสีเฉลี่ยของภาพผ่าน Javascript


118

ไม่แน่ใจว่าเป็นไปได้ แต่ต้องการเขียนสคริปต์ที่จะคืนค่าเฉลี่ยhexหรือrgbค่าสำหรับรูปภาพ ฉันรู้ว่าสามารถทำได้ใน AS แต่ต้องการทำใน JavaScript


19
โบอาสเพื่อนของฉันเคยไปเส้นทางนี้มาก่อน (หวังว่าเขาจะตีระฆัง) แต่โดยเฉลี่ยแล้วมักจะทำให้คุณมีสีเหมือนอาเจียน สิ่งที่คุณต้องการจริงๆคือ "สีเด่น" มีสองสามวิธีในการทำเช่นนั้น (ฮิสโตแกรมสี w / การจัดเก็บข้อมูลแบบไดนามิก ฯลฯ ) แต่ฉันคิดว่าน่าจะแม่นยำกว่าสำหรับผลลัพธ์ที่คุณตั้งใจไว้
Paul Irish

คำตอบ:


149

AFAIK วิธีเดียวที่จะทำได้คือ<canvas/>...

สาธิต V2 : http://jsfiddle.net/xLF38/818/

หมายเหตุสิ่งนี้จะใช้ได้เฉพาะกับรูปภาพในโดเมนเดียวกันและในเบราว์เซอร์ที่รองรับผ้าใบ 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;

}

สำหรับ IE ตรวจสอบexcanvas


1
มีวิธีใดบ้างในการรับฮิสโตแกรมสีสำหรับรูปภาพที่อยู่บนโดเมนอื่น
Dan Loewenherz

1
Dan: ฉันคิดว่าคุณมีข้อ จำกัด ข้ามแหล่งกำเนิดหากคุณพยายามเข้าถึงข้อมูลรูปภาพบนรูปภาพจากโดเมนอื่น ซึ่งเป็นคนเกียจคร้าน.

8
ผมคิดว่ามีตำแหน่ง woring สำหรับค่าสีฟ้าและสีเขียวที่คุณเขียนและที่ควรจะ'rgb('+rgb.r+','+rgb.b+','+rgb.g+')' 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'มันแปลกเมื่อสีที่โดดเด่นเป็นสีน้ำเงิน แต่ผลลัพธ์เป็นสีเขียว :)
Ariona Rian

7
พบวิธีแก้ปัญหาที่ดีสำหรับข้อ จำกัด ข้ามแหล่งที่มา: เพียงเพิ่มimg.crossOrigin = '';ก่อนตั้งค่าsrcแอตทริบิวต์ พบเมื่อ: coderwall.com/p/pa-2uw
mhu

ไม่ต้องไปนับตั้งแต่count === length / 4 / blockSize;)
yckart

84

คิดว่าฉันโพสต์โปรเจ็กต์ที่เพิ่งเจอเพื่อให้ได้สีที่โดดเด่น:

โจรสี

สคริปต์สำหรับการจับสีที่โดดเด่นหรือจานสีที่เป็นตัวแทนจากรูปภาพ ใช้ javascript และ canvas

วิธีแก้ปัญหาอื่น ๆ ที่กล่าวถึงและแนะนำสีที่โดดเด่นไม่เคยตอบคำถามในบริบทที่เหมาะสม ("ในจาวาสคริปต์") หวังว่าโครงการนี้จะช่วยให้ผู้ที่ต้องการทำเช่นนั้น


55

"เด่นสี" เป็นเรื่องยุ่งยาก สิ่งที่คุณต้องการทำคือเปรียบเทียบระยะห่างระหว่างแต่ละพิกเซลกับพิกเซลอื่น ๆ ในพื้นที่สี (ระยะทางแบบยุคลิด) จากนั้นค้นหาพิกเซลที่มีสีใกล้เคียงกับสีอื่น ๆ มากที่สุด พิกเซลนั้นเป็นสีที่โดดเด่น สีโดยเฉลี่ยมักจะเป็นโคลน

ฉันหวังว่าฉันจะมี MathML ที่นี่เพื่อแสดงระยะทางแบบยุคลิด Google มัน

ฉันดำเนินการข้างต้นสำเร็จในพื้นที่สี RGB โดยใช้ PHP / GD ที่นี่: https://gist.github.com/cf23f8bddb307ad4abd8

อย่างไรก็ตามสิ่งนี้มีราคาแพงมากในการคำนวณ มันจะทำให้ระบบของคุณพังในภาพขนาดใหญ่และจะทำให้เบราว์เซอร์ของคุณพังแน่นอนหากคุณลองใช้ในไคลเอนต์ ฉันกำลังดำเนินการปรับโครงสร้างการดำเนินการของฉันเป็น: - เก็บผลลัพธ์ไว้ในตารางการค้นหาเพื่อใช้ในการทำซ้ำในแต่ละพิกเซลในอนาคต - เพื่อแบ่งภาพขนาดใหญ่ออกเป็นกริดขนาด 20px 20px สำหรับการครอบงำแบบโลคัลไลซ์ - ใช้ระยะห่างแบบยุคลิดระหว่าง x1y1 และ x1y2 เพื่อหาระยะห่างระหว่าง x1y1 และ x1y3

โปรดแจ้งให้เราทราบหากคุณมีความคืบหน้าในส่วนนี้ ฉันจะมีความสุขที่ได้เห็นมัน ฉันจะทำเช่นเดียวกัน

Canvas เป็นวิธีที่ดีที่สุดในไคลเอนต์ SVG ไม่ใช่ SVG เป็นเวกเตอร์ หลังจากที่ฉันได้รับการดำเนินการแล้วสิ่งต่อไปที่ฉันต้องการทำคือทำให้สิ่งนี้ทำงานบนผืนผ้าใบ (อาจใช้ Webworker สำหรับการคำนวณระยะทางโดยรวมของแต่ละพิกเซล)

สิ่งที่ต้องคิดอีกอย่างคือ RGB ไม่ใช่พื้นที่สีที่ดีสำหรับการทำเช่นนี้เนื่องจากระยะห่างระหว่างสีแบบยูคลิดในพื้นที่ RGB นั้นไม่ใกล้กับระยะภาพมากนัก พื้นที่สีที่ดีกว่าสำหรับการทำเช่นนี้อาจเป็น LUV แต่ฉันไม่พบไลบรารีที่ดีสำหรับสิ่งนี้หรือ algorythims ใด ๆ สำหรับการแปลง RGB เป็น LUV

วิธีการที่แตกต่างกันอย่างสิ้นเชิงคือการจัดเรียงสีของคุณเป็นสีรุ้งและสร้างฮิสโตแกรมที่มีความทนทานต่อเฉดสีที่แตกต่างกัน ฉันไม่ได้ลองสิ่งนี้เพราะการเรียงสีในรุ้งนั้นยากและฮิสโทแกรมของสีก็เช่นกัน ฉันอาจลองสิ่งนี้ต่อไป โปรดแจ้งให้เราทราบอีกครั้งหากคุณมีความคืบหน้าที่นี่


6
นี่เป็นหนึ่งในคำตอบที่ฉันหวังว่าจะทำได้มากกว่า +1 :-)
David Johnstone

3
คำตอบที่ดีเยี่ยมแม้ว่าจะไม่มีการแสดงผลคำตอบที่ชัดเจน สิ่งนี้จะช่วยให้ฉันตัดสินใจในขั้นตอนต่อไป ...
bgreater

นี่คือคำตอบที่ดี ฉันเขียนโมดูลโหนดสองสามตัวเพื่อทำการคำนวณระยะทางแบบยูคลิดไม่ใช่ใน LUV แต่อยู่ในพื้นที่สีL a b * ดูgithub.com/zeke/color-namerและgithub.com/zeke/euclidean-distance
Zeke

1
@ ผู้ใช้: ฉันแค่สงสัยว่า: จะไม่พูดว่าตัวอย่าง 1% เพียงพอสำหรับภาพขนาดใหญ่เพื่อเร่งความเร็วในการคำนวณหรือไม่?
jackthehipster

ดูเหมือนค่ามัธยฐานทางเรขาคณิต อาจจะช่วยได้? stackoverflow.com/questions/12934213/… และหากจุดที่คุณต้องการต้องมีอยู่ในชุดเดิม: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence

16

ขั้นแรก: สามารถทำได้โดยไม่ต้องใช้ผ้าใบ HTML5 หรือ SVG
อันที่จริงคนที่เพิ่งมีการจัดการเพื่อสร้างไฟล์ PNG ฝั่งไคลเอ็นต์โดยใช้ JavaScript , โดยไม่ต้องผ้าใบหรือ SVG โดยใช้ข้อมูลโครงการ URI

ประการที่สอง: คุณอาจไม่จำเป็นต้องใช้ Canvas, SVG หรืออย่างใดอย่างหนึ่งเลย
หากคุณต้องการประมวลผลรูปภาพในฝั่งไคลเอ็นต์เท่านั้นโดยไม่ต้องแก้ไขทั้งหมดนี้ก็ไม่จำเป็นต้องใช้

คุณสามารถรับที่อยู่ต้นทางได้จากแท็ก img บนหน้าทำการร้องขอXHR โดยส่วนใหญ่อาจมาจากแคชของเบราว์เซอร์และประมวลผลเป็นไบต์สตรีมจาก Javascript
คุณจะต้องมีความเข้าใจที่ดีเกี่ยวกับรูปแบบภาพ (ตัวสร้างข้างต้นบางส่วนขึ้นอยู่กับแหล่งที่มาของ libpng และอาจเป็นจุดเริ่มต้นที่ดี)


+1 สำหรับโซลูชัน bytestream นี่เป็นวิธีที่ดีหากมีทางเลือกเดียวคือทำให้แอปพลิเคชันไคลเอนต์
loretoparisi

11

ฉันจะพูดผ่านแท็กผ้าใบ HTML

คุณสามารถค้นหาโพสต์โดย @Georg ได้ที่นี่ซึ่งพูดถึงโค้ดขนาดเล็กโดย Opera dev:

// 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);

วิธีนี้จะกลับภาพโดยใช้ค่า R, G และ B ของแต่ละพิกเซล คุณสามารถจัดเก็บค่า RGB ได้อย่างง่ายดายจากนั้นปัดเศษอาร์เรย์สีแดงสีเขียวและสีน้ำเงินและในที่สุดก็แปลงกลับเป็นรหัส HEX


มีโอกาสใดบ้างที่จะมีทางเลือก IE ให้กับโซลูชันผ้าใบ?
bgreater

@ Ben4Himv: มีตัวอย่างแพลตฟอร์ม IE9 ;-) แต่อย่างจริงจังฉันไม่กลัว
Andy E


4

Javascript ไม่สามารถเข้าถึงข้อมูลสีแต่ละพิกเซลของรูปภาพ อย่างน้อยก็อาจจะไม่ถึง html5 ... เมื่อถึงจุดนั้นก็เป็นเหตุผลว่าคุณจะสามารถวาดภาพลงบนผืนผ้าใบจากนั้นตรวจสอบผืนผ้าใบ (บางทีฉันไม่เคยทำเอง)


2

โซลูชัน All-In-One

โดยส่วนตัวแล้วฉันจะรวมColor Thief เข้ากับName that Colourเวอร์ชันแก้ไขนี้เพื่อให้ได้ผลลัพธ์สีที่โดดเด่นมากเกินพอสำหรับรูปภาพ

ตัวอย่าง:

พิจารณาภาพต่อไปนี้:

ใส่คำอธิบายภาพที่นี่

คุณสามารถใช้รหัสต่อไปนี้เพื่อดึงข้อมูลภาพที่เกี่ยวข้องกับสีเด่น:

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

เกี่ยวกับ "Color Quantization" ซึ่งมีหลายวิธีเช่นMMCQ (Modified Median Cut Quantization) หรือ OQ (Octree Quantization) วิธีการต่างๆใช้K-Meansเพื่อให้ได้กลุ่มสี

ฉันได้รวบรวมทั้งหมดไว้ที่นี่เนื่องจากฉันกำลังหาวิธีแก้ปัญหาtvOSที่มี XHTML เป็นส่วนย่อยซึ่งไม่มี<canvas/>องค์ประกอบ:

สร้างสีที่โดดเด่นสำหรับภาพ RGB ด้วย XMLHttpRequest


1

วิธีที่แม่นยำน้อยกว่า แต่เร็วที่สุดในการรับสีเฉลี่ยของภาพด้วยdatauriการสนับสนุน:

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

ตามที่ระบุไว้ในคำตอบอื่น ๆ บ่อยครั้งที่คุณต้องการสีที่โดดเด่นเมื่อเทียบกับสีทั่วไปซึ่งมีแนวโน้มที่จะเป็นสีน้ำตาล ฉันเขียนสคริปต์ที่ได้รับสีที่พบบ่อยที่สุดและโพสต์ไว้ในส่วนสำคัญนี้


-2

มีเครื่องมือออนไลน์pickimagecolor.comที่ช่วยให้คุณค้นหาค่าเฉลี่ยหรือสีที่โดดเด่นของภาพคุณเพียงแค่ต้องอัปโหลดภาพจากคอมพิวเตอร์ของคุณจากนั้นคลิกที่ภาพ ให้สีเฉลี่ยใน HEX, RGB และ HSV นอกจากนี้ยังพบเฉดสีที่ตรงกับสีนั้น ๆ ให้เลือกอีกด้วย ฉันใช้มันหลายครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.