단일 픽셀의 색상이 아닌 직사각형 영역의 평균 색상을 얻으려면 다음 다른 질문을 살펴보십시오.
👉 JavaScript-이미지의 특정 영역에서 평균 색상 가져 오기
어쨌든 둘 다 매우 비슷한 방식으로 수행됩니다.
🔍 이미지 또는 캔버스에서 단일 픽셀의 색상 / 값 가져 오기
단일 픽셀의 색상을 얻으려면 먼저 해당 이미지를 이미 수행 한 캔버스에 그립니다.
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;
🚀 모든 이미지 데이터를 한 번에 가져옴으로써 속도 향상
이 동일한 CanvasRenderingContext2D.getImageData () 를 사용 하여 전체 이미지의 값을 가져와야 합니다.이 작업은 세 번째 및 네 번째 매개 변수를 변경하여 수행합니다. 해당 기능의 시그니처는 다음과 같습니다.
ImageData ctx.getImageData(sx, sy, sw, sh);
sx
: ImageData를 추출 할 직사각형의 왼쪽 상단 모서리의 x 좌표입니다.
sy
: ImageData를 추출 할 사각형의 왼쪽 위 모서리의 y 좌표입니다.
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;
const R = imageDataData[i];
const G = imageDataData[i + 1];
const B = imageDataData[i + 2];
const A = imageDataData[i + 3] / 255;
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;
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
외부 이미지를 포함하거나 더 긴 데이터 URI를 사용하려고 할 때 허용되는 것보다 큰 답변을 포함하는 경우 문제 를 피하기 위해 작은 데이터 URI 를 사용하고 있습니다.
🕵️이 색상은 이상해 보이지 않습니까?
별표 모양의 테두리 주위로 커서를 이동하면 때때로 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;
픽셀이 더 투명할수록 ( A
0에 가까울수록) 색상이 더 밝아집니다.