Mousemove पर कैनवास से पिक्सेल रंग प्राप्त करें


85

क्या माउस के तहत RGB मूल्य पिक्सेल प्राप्त करना संभव है? क्या इसका पूरा उदाहरण है? यहाँ मेरे पास अभी तक क्या है:

function draw() {
      var ctx = document.getElementById('canvas').getContext('2d');
      var img = new Image();
      img.src = 'Your URL';

      img.onload = function(){
        ctx.drawImage(img,0,0);


      };

      canvas.onmousemove = function(e) {
            var mouseX, mouseY;

            if(e.offsetX) {
                mouseX = e.offsetX;
                mouseY = e.offsetY;
            }
            else if(e.layerX) {
                mouseX = e.layerX;
                mouseY = e.layerY;
            }
            var c = ctx.getImageData(mouseX, mouseY, 1, 1).data;
            
            $('#ttip').css({'left':mouseX+20, 'top':mouseY+20}).html(c[0]+'-'+c[1]+'-'+c[2]);
      };
    }

का डुप्लीकेट stackoverflow.com/questions/1936021/...
bcoughlan

जवाबों:


150

यहाँ एक पूर्ण, आत्म-निहित उदाहरण है। सबसे पहले, निम्न HTML का उपयोग करें:

<canvas id="example" width="200" height="60"></canvas>
<div id="status"></div>

फिर यादृच्छिक पृष्ठभूमि रंगों के साथ कैनवास पर कुछ वर्ग रखें:

var example = document.getElementById('example');
var context = example.getContext('2d');
context.fillStyle = randomColor();
context.fillRect(0, 0, 50, 50);
context.fillStyle = randomColor();
context.fillRect(55, 0, 50, 50);
context.fillStyle = randomColor();
context.fillRect(110, 0, 50, 50);

और माउसओवर पर प्रत्येक रंग को प्रिंट करें:

$('#example').mousemove(function(e) {
    var pos = findPos(this);
    var x = e.pageX - pos.x;
    var y = e.pageY - pos.y;
    var coord = "x=" + x + ", y=" + y;
    var c = this.getContext('2d');
    var p = c.getImageData(x, y, 1, 1).data; 
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    $('#status').html(coord + "<br>" + hex);
});

उपरोक्त कोड jQuery की उपस्थिति और निम्नलिखित उपयोगिता कार्यों को मानता है:

function findPos(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}

function randomInt(max) {
  return Math.floor(Math.random() * max);
}

function randomColor() {
    return `rgb(${randomInt(256)}, ${randomInt(256)}, ${randomInt(256)})`
}

इसे यहां कार्रवाई में देखें:


क्या आप कृपया इस प्रश्न को देख सकते हैं और देख सकते हैं कि क्या इसका कोई समाधान है। मैं बहुत सराहना करूंगा कि :)
खावर जीशान

नेस्टेड offsetParents का आपका उपयोग उस बारे में जाने का एक बहुत अच्छा तरीका है। मैंने उसके बारे में कभी नहीं सोचा है। लेकिन आप एक के whileबजाय एक नियमित लूप का उपयोग क्यों नहीं करते हैं ifऔर फिर ए do...while?
जोनाथन लाम

यह उत्तर आज (1-सितंबर-2017) में मेरी मदद करता है ।so +1
अलाइव

@AlivetoDie # 100! धन्यवाद :)
वेन

12

मैं जानता हूं कि यह एक पुराना सवाल है, लेकिन यहां एक विकल्प है। मैं उस छवि डेटा को एक सरणी में संग्रहीत करूंगा, फिर, कैनवास पर माउस ले जाने की घटना पर:

var index = (Math.floor(y) * canvasWidth + Math.floor(x)) * 4
var r = data[index]
var g = data[index + 1]
var b = data[index + 2]
var a = data[index + 3]

हर बार इमेजडेटा प्राप्त करने की तुलना में बहुत आसान है।


7

StackOverflow (उपरोक्त लेख सहित) और अन्य साइटों में यहाँ पाए गए विभिन्न संदर्भों को मिलाते हुए, मैंने जावास्क्रिप्ट और JQuery का उपयोग करते हुए ऐसा किया:

<html>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script src="jquery.js"></script>
<script type="text/javascript">
    window.onload = function(){
        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');
        var img = new Image();
        img.src = 'photo_apple.jpg';
        context.drawImage(img, 0, 0);
    };

    function findPos(obj){
    var current_left = 0, current_top = 0;
    if (obj.offsetParent){
        do{
            current_left += obj.offsetLeft;
            current_top += obj.offsetTop;
        }while(obj = obj.offsetParent);
        return {x: current_left, y: current_top};
    }
    return undefined;
    }

    function rgbToHex(r, g, b){
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
    }

$('#myCanvas').click(function(e){
    var position = findPos(this);
    var x = e.pageX - position.x;
    var y = e.pageY - position.y;
    var coordinate = "x=" + x + ", y=" + y;
    var canvas = this.getContext('2d');
    var p = canvas.getImageData(x, y, 1, 1).data;
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    alert("HEX: " + hex);
});
</script>
<img src="photo_apple.jpg"/>
</body>
</html>

यह मेरा पूर्ण समाधान है। यहां मैंने केवल कैनवास और एक छवि का उपयोग किया है, लेकिन यदि आपको <map>छवि पर उपयोग करने की आवश्यकता है , तो यह भी संभव है।


5

कॉलिंग getImageData हर बार प्रक्रिया को धीमा कर देगा ... चीजों को गति देने के लिए मैं स्टोर छवि डेटा की सिफारिश करता हूं और फिर आप आसानी से और जल्दी से पिक्स मूल्य प्राप्त कर सकते हैं, इसलिए बेहतर प्रदर्शन के लिए ऐसा कुछ करें।

// keep it global
let imgData = false;  // initially no image data we have

// create some function block 
if(imgData === false){   
  // fetch once canvas data     
  var ctx = canvas.getContext("2d");
  imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
    // Prepare your X Y coordinates which you will be fetching from your mouse loc
    let x = 100;   // 
    let y = 100;
    // locate index of current pixel
    let index = (y * imgData.width + x) * 4;

        let red = imgData.data[index];
        let green = imgData.data[index+1];
        let blue = imgData.data[index+2];
        let alpha = imgData.data[index+3];
   // Output
   console.log('pix x ' + x +' y '+y+ ' index '+index +' COLOR '+red+','+green+','+blue+','+alpha);

बहुत उपयोगी +1
वेन

3

शीघ्र जवाब

context.getImageData(x, y, 1, 1).data;एक आरजीबी सरणी देता है। जैसे[50, 50, 50, 255]


यहाँ @ lwburk के rgbToHex फ़ंक्शन का एक संस्करण है जो rgba सरणी को तर्क के रूप में लेता है।

function rgbToHex(rgb){
  return '#' + ((rgb[0] << 16) | (rgb[1] << 8) | rgb[2]).toString(16);
};

यह केवल 16 से ऊपर के लाल-मानों के साथ काम करता है, उदाहरण के लिए [10, 42, 67, 255]उत्पादन #a2a43जो एक मान्य / अच्छी तरह से स्वरूपित हेक्स रंग कोड नहीं है।
तिवारी हौसमैन

3

यदि आपको एक पिक्सेल के रंग के बजाय एक आयताकार क्षेत्र का औसत रंग प्राप्त करने की आवश्यकता है, तो कृपया एक दूसरे से नज़र डालें:

Average जावास्क्रिप्ट - एक छवि के एक निश्चित क्षेत्र से औसत रंग प्राप्त करें

वैसे भी, दोनों एक ही तरह से किए जाते हैं:

Can एक छवि या कैनवास से एक एकल पिक्सेल का रंग / मूल्य प्राप्त करना

एकल पिक्सेल का रंग प्राप्त करने के लिए, आप सबसे पहले उस चित्र को एक कैनवास पर खींचेंगे, जिसे आप पहले ही कर चुके हैं:

const image = document.getElementById('image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = image.width;
const height = image.height;

canvas.width = width;
canvas.height = height;

context.drawImage(image, 0, 0, width, height);

और फिर इस तरह एकल पिक्सेल का मूल्य प्राप्त करें:

const data = context.getImageData(X, Y, 1, 1).data;

// RED   = data[0]
// GREEN = data[1]
// BLUE  = data[2]
// ALPHA = data[3]

D एक बार में सभी ImageData प्राप्त करके गति को तेज करना

संपूर्ण छवि के मान प्राप्त करने के लिए आपको इसी CanvasRenderingContext2D.getImageData () का उपयोग करने की आवश्यकता है , जो आप इसके तीसरे और चौथे परिमार्जन को बदलकर करते हैं। उस फ़ंक्शन का हस्ताक्षर है:

ImageData ctx.getImageData(sx, sy, sw, sh);
  • sx: आयत के ऊपरी बाएँ कोने का x निर्देशांक जहाँ से ImageData निकाला जाएगा।
  • sy: आयत के ऊपरी बाएँ कोने का y निर्देशांक जिसमें से ImageData निकाला जाएगा।
  • sw: आयत की चौड़ाई जिससे ImageData निकाला जाएगा।
  • sh: आयत की ऊँचाई जिससे ImageData निकाला जाएगा।

आप देख सकते हैं कि यह एक ImageDataवस्तु देता है , जो कुछ भी है । यहां महत्वपूर्ण बात यह है कि उस वस्तु में एक .dataसंपत्ति होती है जिसमें हमारे सभी पिक्सेल मूल्य होते हैं।

हालांकि, ध्यान दें कि .dataसंपत्ति 1-आयाम है Uint8ClampedArray, जिसका अर्थ है कि सभी पिक्सेल के घटक समतल हो गए हैं, इसलिए आपको ऐसा कुछ मिल रहा है जो इस तरह दिखता है:

मान लीजिए कि आपकी 2x2 छवि इस प्रकार है:

 RED PIXEL |       GREEN PIXEL
BLUE PIXEL | TRANSPARENT PIXEL

फिर, आप उन्हें इस तरह मिलेगा:

[ 255, 0, 0, 255,    0, 255, 0, 255,    0, 0, 255, 255,    0, 0, 0, 0          ]
|   RED PIXEL   |    GREEN PIXEL   |     BLUE PIXEL   |    TRANSPAERENT  PIXEL |
|   1ST PIXEL   |      2ND PIXEL   |      3RD PIXEL   |             4TH  PIXEL | 

चूंकि कॉलिंग getImageDataएक धीमा ऑपरेशन है, आप इसे केवल एक बार कॉल कर सकते हैं ताकि सभी छवि ( sw= छवि चौड़ाई, sh= छवि ऊंचाई) का डेटा प्राप्त किया जा सके ।

फिर, ऊपर के उदाहरण में, आप के घटकों का उपयोग करना चाहते हैं TRANSPARENT PIXEL, यह है कि, के स्थान पर एक x = 1, y = 1काल्पनिक छवि के हैं, तो आप अपनी पहली सूचकांक मिलेगा iअपने में ImageDataके dataरूप में संपत्ति:

const i = (y * imageData.width + x) * 4;

✨ आइए इसे एक्शन में देखें

const solidColor = document.getElementById('solidColor');
const alphaColor = document.getElementById('alphaColor');
const solidWeighted = document.getElementById('solidWeighted');

const solidColorCode = document.getElementById('solidColorCode');
const alphaColorCode = document.getElementById('alphaColorCode');
const solidWeightedCOde = document.getElementById('solidWeightedCode');

const brush = document.getElementById('brush');
const image = document.getElementById('image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = image.width;
const height = image.height;

const BRUSH_SIZE = brush.offsetWidth;
const BRUSH_CENTER = BRUSH_SIZE / 2;
const MIN_X = image.offsetLeft + 4;
const MAX_X = MIN_X + width - 1;
const MIN_Y = image.offsetTop + 4;
const MAX_Y = MIN_Y + height - 1;

canvas.width = width;
canvas.height = height;

context.drawImage(image, 0, 0, width, height);

const imageDataData = context.getImageData(0, 0, width, height).data;

function sampleColor(clientX, clientY) {
  if (clientX < MIN_X || clientX > MAX_X || clientY < MIN_Y || clientY > MAX_Y) {
    requestAnimationFrame(() => {
      brush.style.transform = `translate(${ clientX }px, ${ clientY }px)`;
      solidColorCode.innerText = solidColor.style.background = 'rgb(0, 0, 0)';
      alphaColorCode.innerText = alphaColor.style.background = 'rgba(0, 0, 0, 0.00)';
      solidWeightedCode.innerText = solidWeighted.style.background = 'rgb(0, 0, 0)';
    });
    
    return;
  }
  
  const imageX = clientX - MIN_X;
  const imageY = clientY - MIN_Y;
  
  const i = (imageY * width + imageX) * 4;

  // A single pixel (R, G, B, A) will take 4 positions in the array:
  const R = imageDataData[i];
  const G = imageDataData[i + 1];
  const B = imageDataData[i + 2];
  const A = imageDataData[i + 3] / 255;
  const iA = 1 - A;

  // Alpha-weighted color:
  const wR = (R * A + 255 * iA) | 0;
  const wG = (G * A + 255 * iA) | 0;
  const wB = (B * A + 255 * iA) | 0;

  // Update UI:
  
  requestAnimationFrame(() => {
    brush.style.transform = `translate(${ clientX }px, ${ clientY }px)`;

    solidColorCode.innerText = solidColor.style.background
      = `rgb(${ R }, ${ G }, ${ B })`;

    alphaColorCode.innerText = alphaColor.style.background
      = `rgba(${ R }, ${ G }, ${ B }, ${ A.toFixed(2) })`;

    solidWeightedCode.innerText = solidWeighted.style.background
      = `rgb(${ wR }, ${ wG }, ${ wB })`;
  });
}

document.onmousemove = (e) => sampleColor(e.clientX, e.clientY);
  
sampleColor(MIN_X, MIN_Y);
body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  cursor: none;
  font-family: monospace;
  overflow: hidden;
}

#image {
  border: 4px solid white;
  border-radius: 2px;
  box-shadow: 0 0 32px 0 rgba(0, 0, 0, .25);
  width: 150px;
  box-sizing: border-box;
}

#brush {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  width: 1px;
  height: 1px;
  mix-blend-mode: exclusion;
  border-radius: 100%;
}

#brush::before,
#brush::after {
  content: '';
  position: absolute;
  background: magenta;
}

#brush::before {
  top: -16px;
  left: 0;
  height: 33px;
  width: 100%;
}

#brush::after {
  left: -16px;
  top: 0;
  width: 33px;
  height: 100%;
}

#samples {
  position: relative;
  list-style: none;
  padding: 0;
  width: 250px;
}

#samples::before {
  content: '';
  position: absolute;
  top: 0;
  left: 27px;
  width: 2px;
  height: 100%;
  background: black;
  border-radius: 1px;
}

#samples > li {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-left: 56px;
}

#samples > li + li {
  margin-top: 8px;
}

.sample {
  position: absolute;
  top: 50%;
  left: 16px;
  transform: translate(0, -50%);
  display: block;
  width: 24px;
  height: 24px;
  border-radius: 100%;
  box-shadow: 0 0 16px 4px rgba(0, 0, 0, .25);  
  margin-right: 8px;
}

.sampleLabel {
  font-weight: bold;
  margin-bottom: 8px;
}

.sampleCode {
  
}
<img id="image" src="data:image/gif;base64,R0lGODlhSwBLAPEAACMfIO0cJAAAAAAAACH/C0ltYWdlTWFnaWNrDWdhbW1hPTAuNDU0NTUAIf4jUmVzaXplZCBvbiBodHRwczovL2V6Z2lmLmNvbS9yZXNpemUAIfkEBQAAAgAsAAAAAEsASwAAAv+Uj6mb4A+QY7TaKxvch+MPKpC0eeUUptdomOzJqnLUvnFcl7J6Pzn9I+l2IdfII8DZiCnYsYdK4qRTptAZwQKRVK71CusOgx2nFRrlhMu+33o2NEalC6S9zQvfi3Mlnm9WxeQ396F2+HcQsMjYGEBRVbhy5yOp6OgIeVIHpEnZyYCZ6cklKBJX+Kgg2riqKoayOWl2+VrLmtDqBptIOjZ6K4qAeSrL8PcmHExsgMs2dpyIxPpKvdhM/YxaTMW2PGr9GP76BN3VHTMurh7eoU14jsc+P845Vn6OTb/P/I68iYOfwGv+JOmRNHBfsV5ujA1LqM4eKDoNvXyDqItTxYX/DC9irKBlIhkKGPtFw1JDiMeS7CqWqySPZcKGHH/JHGgIpb6bCl1O0LmT57yCOqoI5UcU0YKjPXmFjMm0ZQ4NIVdGBdZRi9WrjLxJNMY1Yr4dYeuNxWApl1ALHb+KDHrTV1owlriedJgSr4Cybu/9dFiWYAagsqAGVkkzaZTAuqD9ywKWMUG9dCO3u2zWpVzIhpW122utZlrHnTN+Bq2Mqrlnqh8CQ+0Mrq3Kc++q7eo6dlB3rLuh3abPVbbbI2mxBdhWdsZhid8cr0oy9F08q0k5FXSadiyL1mF5z51a8VsQOp3/LlodkBfzmzWf2bOrtfzr48k/1hupDaLa9rUbO+zlwndfaOCURAXRNaCBqBT2BncJakWfTzSYkmCEFr60RX0V8sKaHOltCBJ1tAAFYhHaVVbig3jxp0IBADs=" >

<div id="brush"></div>

<ul id="samples">
  <li>
    <span class="sample" id="solidColor"></span>
    <div class="sampleLabel">solidColor</div>
    <div class="sampleCode" id="solidColorCode">rgb(0, 0, 0)</div>
  </li>
  <li>
    <span class="sample" id="alphaColor"></span>
    <div class="sampleLabel">alphaColor</div>
    <div class="sampleCode" id="alphaColorCode">rgba(0, 0, 0, 0.00)</div>
  </li>
  <li>
    <span class="sample" id="solidWeighted"></span>
    <div class="sampleLabel">solidWeighted (with white)</div>
    <div class="sampleCode" id="solidWeightedCode">rgb(0, 0, 0)</div>
  </li>
</ul>

⚠️ ध्यान दें कि मैं एक छोटे डेटा यूआरआई का उपयोग कर रहा हूं Cross-Originयदि मैं एक बाहरी छवि या एक उत्तर को शामिल करता हूं, तो अगर मैं एक लंबे डेटा यूआरआई का उपयोग करने की कोशिश करता हूं तो यह एक बड़ा सवाल है।

🕵️ ये रंग अजीब लगते हैं, नहीं?

यदि आप तारांकन आकृति की सीमाओं के चारों ओर कर्सर ले जाते हैं, तो आप देखेंगे कि कभी-कभी avgSolidColorलाल होता है, लेकिन आप जिस पिक्सेल का नमूना ले रहे हैं वह सफेद दिखता है। ऐसा इसलिए है क्योंकि भले ही Rउस पिक्सेल के लिए घटक अधिक हो, अल्फा चैनल कम है, इसलिए रंग वास्तव में लाल रंग की लगभग पारदर्शी छाया है, लेकिन avgSolidColorउस पर ध्यान नहीं देता है।

दूसरी ओर, avgAlphaColorगुलाबी दिखता है। खैर, यह वास्तव में सच नहीं है, यह सिर्फ गुलाबी दिखता है क्योंकि हम अब अल्फा चैनल का उपयोग कर रहे हैं, जो इसे अर्ध-पारदर्शी बनाता है और हमें पृष्ठ की पृष्ठभूमि को देखने की अनुमति देता है, जो इस मामले में सफेद है।

🎨 अल्फा-भारित रंग

फिर, हम इसे ठीक करने के लिए क्या कर सकते हैं? ठीक है, यह पता चलता है कि हमें अपने नए नमूने के घटकों की गणना करने के लिए अल्फा चैनल और इसके व्युत्क्रम को भार के रूप में उपयोग करने की आवश्यकता है, इस मामले में इसे सफेद के साथ विलय कर दिया जाता है, क्योंकि यह वह रंग है जिसे हम पृष्ठभूमि के रूप में उपयोग करते हैं।

इसका मतलब है कि यदि कोई पिक्सेल है R, G, B, A, जहां Aअंतराल में है [0, 1], तो हम अल्फा चैनल के व्युत्क्रम की गणना करेंगे iA, और भारित नमूने के घटक निम्नानुसार होंगे:

const iA = 1 - A;
const wR = (R * A + 255 * iA) | 0;
const wG = (G * A + 255 * iA) | 0;
const wB = (B * A + 255 * iA) | 0;

ध्यान दें कि एक पिक्सेल कितना पारदर्शी है ( A0 के करीब), हल्का रंग।



1

मेरे पास कैनवास से पिक्सेल रंग प्राप्त करने का एक बहुत ही सरल काम उदाहरण है।

पहले कुछ बुनियादी HTML:

<canvas id="myCanvas" width="400" height="250" style="background:red;" onmouseover="echoColor(event)">
</canvas>

फिर कैनवस पर कुछ आकर्षित करने और रंग पाने के लिए जेएस:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(10, 10, 50, 50);

function echoColor(e){
    var imgData = ctx.getImageData(e.pageX, e.pageX, 1, 1);
    red = imgData.data[0];
    green = imgData.data[1];
    blue = imgData.data[2];
    alpha = imgData.data[3];
    console.log(red + " " + green + " " + blue + " " + alpha);  
}

यहाँ एक कार्यशील उदाहरण है , बस कंसोल को देखें।


0

@Wayne Burkett का जवाब अच्छा है। यदि आप rgba रंग पाने के लिए अल्फा मान भी निकालना चाहते हैं, तो हम यह कर सकते हैं:

var r = p[0], g = p[1], b = p[2], a = p[3] / 255;
var rgba = "rgb(" + r + "," + g + "," + b + "," + a + ")";

मैंने अल्फा मान को 255 से विभाजित किया है क्योंकि ImageData ऑब्जेक्ट इसे 0 - 255 के बीच पूर्णांक के रूप में संग्रहीत करता है, लेकिन अधिकांश एप्लिकेशन (उदाहरण के लिए CanvasRenderingContext2D.fillRect()) को वैध सीएसएस प्रारूप में रंगों की आवश्यकता होती है, जहां अल्फा मान 0 और 1 के बीच है।

(यह भी याद रखें कि यदि आप पारदर्शी रंग निकालते हैं और फिर इसे कैनवास पर वापस खींचते हैं, तो यह पहले से जो भी रंग है, उसे ओवरले कर देगा। इसलिए यदि आप rgba(0,0,0,0.1)10 बार उसी स्थान पर रंग फेंकते हैं , तो यह काला होगा।)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.