Canvas в HTML5 контрастность изображения
я писал программу обработки изображений, которая применяет эффекты через обработку пикселей холста HTML5. Я достиг молотилки, Vintaging и ColorGradient пиксельных манипуляций, но невероятно, я не могу изменить контраст изображения! Я пробовал несколько решений, но я всегда получаю слишком много яркости на картинке и меньше контрастного эффекта и я не планирую использовать библиотеки Javascript, так как я пытаюсь достичь этих эффектов прирожденно.
основной код манипуляции пикселями:
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
//Note: data[i], data[i+1], data[i+2] represent RGB respectively
data[i] = data[i];
data[i+1] = data[i+1];
data[i+2] = data[i+2];
}
пример манипулирования пикселями
значения находятся в режиме RGB, что означает, что data[i] - красный цвет. Поэтому, если data[i] = data[i] * 2; яркость будет увеличена в два раза для красного канала этого пикселя. Пример:
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
//Note: data[i], data[i+1], data[i+2] represent RGB respectively
//Increases brightness of RGB channel by 2
data[i] = data[i]*2;
data[i+1] = data[i+1]*2;
data[i+2] = data[i+2]*2;
}
*примечание: Я не прошу вас, чтобы завершить код! Это было бы просто одолжением! Я прошу алгоритм (даже псевдо код) показывает, как контраст в пиксельной манипуляции возможен! Я был бы рад, если кто-то может предоставить хороший алгоритм контраста изображений в HTML5 canvas.
7 ответов
более быстрый вариант (на основе Эшера!--73-->) составляет:
function contrastImage(imgData, contrast){ //input range [-100..100]
var d = imgData.data;
contrast = (contrast/100) + 1; //convert to decimal & shift range: [0..2]
var intercept = 128 * (1 - contrast);
for(var i=0;i<d.length;i+=4){ //r,g,b,a
d[i] = d[i]*contrast + intercept;
d[i+1] = d[i+1]*contrast + intercept;
d[i+2] = d[i+2]*contrast + intercept;
}
return imgData;
}
вывод, подобный приведенному ниже; эта версия математически одинакова, но работает намного быстрее.
оригинальный ответ
вот упрощенная версия с объяснением подход уже обсудили (который был основан на этот статья):
function contrastImage(imageData, contrast) { // contrast as an integer percent
var data = imageData.data; // original array modified, but canvas not updated
contrast *= 2.55; // or *= 255 / 100; scale integer percent to full range
var factor = (255 + contrast) / (255.01 - contrast); //add .1 to avoid /0 error
for(var i=0;i<data.length;i+=4) //pixel values in 4-byte blocks (r,g,b,a)
{
data[i] = factor * (data[i] - 128) + 128; //r value
data[i+1] = factor * (data[i+1] - 128) + 128; //g value
data[i+2] = factor * (data[i+2] - 128) + 128; //b value
}
return imageData; //optional (e.g. for filter function chaining)
}
Примечания
я решил использовать
после попытки ответа на Schahriar SaffarShargh, он не ведет себя, как контраст должен вести себя. Я, наконец, наткнулся на этот алгоритм, и он работает как шарм!
для получения дополнительной информации об алгоритме, читать в этой статье и это раздел комментариев.
function contrastImage(imageData, contrast) {
var data = imageData.data;
var factor = (259 * (contrast + 255)) / (255 * (259 - contrast));
for(var i=0;i<data.length;i+=4)
{
data[i] = factor * (data[i] - 128) + 128;
data[i+1] = factor * (data[i+1] - 128) + 128;
data[i+2] = factor * (data[i+2] - 128) + 128;
}
return imageData;
}
использование:
var newImageData = contrastImage(imageData, 30);
надеюсь, это будет экономия времени для кого-то. Ура!
я узнал, что вы должны использовать эффект, отделяя темноты и огни или технически все, что меньше 127 (среднее значение R+G+B / 3) в шкале rgb является черным и более 127 является белым, поэтому по вашему уровню контраста вы минус значение, скажем, 10 контраст от черных и добавить то же значение к белым!
вот пример: У меня есть два пикселя с цветами RGB, [105,40,200] / [255,200,150] Так я знаю, что для моего первого пиксела 105 + 40 + 200 = 345, 345/3 = 115 и 115 меньше моей половины 255, которая составляет 127, поэтому я считаю пиксель ближе к [0,0,0], поэтому, если я хочу минус 10 контраст, то я забираю 10 из каждого цвета в среднем Таким образом, я должен разделить значение каждого цвета на среднее значение итога, которое было 115 для этого случая, и умножить его на мой контраст и минус конечное значение из этого конкретного цвета:
например, я возьму 105 (красный) из моего пикселя, поэтому я разделю его на общий avg RGB. который 115 и умножает его на мой значение контраста 10, (105/115) * 10, которое дает вам что-то около 9 (вы должны округлить его!) а затем уберите это 9 от 105, чтобы цвет стал 96, поэтому мой красный после контрастности 10 на темном пикселе.
поэтому, если я перейду на значения моего пикселя, станьте [96,37,183]! (Примечание: масштаб контраста зависит от вас! но мой в конце концов вы должны преобразовать его в некоторый масштаб, например, от 1 до 255)
для более светлых пикселей я также делаю то же самое, за исключением вычитания контраста значение я добавляю его! и если вы достигнете предела 255 или 0, вы прекратите сложение и вычитание для этого конкретного цвета! поэтому мой второй пиксель, который является более легким пикселем, становится [255,210,157]
как вы добавляете больше контраста он будет светлее цвета и темнее темнее и, следовательно, добавляет контраст к вашей картине!
вот пример кода JavaScript ( я еще не пробовал ) :
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
var contrast = 10;
var average = Math.round( ( data[i] + data[i+1] + data[i+2] ) / 3 );
if (average > 127){
data[i] += ( data[i]/average ) * contrast;
data[i+1] += ( data[i+1]/average ) * contrast;
data[i+2] += ( data[i+2]/average ) * contrast;
}else{
data[i] -= ( data[i]/average ) * contrast;
data[i+1] -= ( data[i+1]/average ) * contrast;
data[i+2] -= ( data[i+2]/average ) * contrast;
}
}
вы можете взглянуть на документы OpenCV, чтобы увидеть, как вы можете это сделать:настройки яркости и контрастности.
тогда есть демо-код:
double alpha; // Simple contrast control: value [1.0-3.0]
int beta; // Simple brightness control: value [0-100]
for( int y = 0; y < image.rows; y++ )
{
for( int x = 0; x < image.cols; x++ )
{
for( int c = 0; c < 3; c++ )
{
new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
}
}
}
который, я полагаю, вы способны перевести на javascript.
по vintaging я предполагаю, что вы пытаетесь применить LUTS..Недавно я пытался добавить цветовые обработки к окнам холста. Если вы хотите фактически применить "LUTS" к окну холста, я считаю, что вам нужно фактически сопоставить массив, который imageData возвращает в массив RGB LUT.
(от иллюзии света) В качестве примера начало 1D LUT может выглядеть примерно так: Примечание: строго говоря, это 3x 1D LUTs, так как каждый цвет (R,G, B) является 1D Лут
R, G, B
3, 0, 0
5, 2, 1
7, 5, 3
9, 9, 9
это означает, что:
For an input value of 0 for R, G, and B, the output is R=3, G=0, B=0
For an input value of 1 for R, G, and B, the output is R=5, G=2, B=1
For an input value of 2 for R, G, and B, the output is R=7, G=5, B=3
For an input value of 3 for R, G, and B, the output is R=9, G=9, B=9
что является странным LUT, но вы видите, что для заданного значения R, G или B ввода существует заданное значение R, G и B вывода.
Итак, если пиксель имеет входное значение 3, 1, 0 для RGB, выходной пиксель будет 9, 2, 0.
во время этого я также понял после игры с imageData, что он возвращает Uint8Array и что значения в этом массиве являются десятичными. Большинство 3D СПОИ наговор. Итак, сначала вы чтобы сделать некоторый тип преобразования hex в dec на всем массиве перед всем этим отображением.
эта реализация javascript соответствует определению SVG / CSS3 "контраст" (и следующий код будет отображать изображение холста одинаково):
/*contrast filter function*/
//See definition at https://drafts.fxtf.org/filters/#contrastEquivalent
//pixels come from your getImageData() function call on your canvas image
contrast = function(pixels, value){
var d = pixels.data;
var intercept = 255*(-value/2 + 0.5);
for(var i=0;i<d.length;i+=4){
d[i] = d[i]*value + intercept;
d[i+1] = d[i+1]*value + intercept;
d[i+2] = d[i+2]*value + intercept;
//implement clamping in a separate function if using in production
if(d[i] > 255) d[i] = 255;
if(d[i+1] > 255) d[i+1] = 255;
if(d[i+2] > 255) d[i+2] = 255;
if(d[i] < 0) d[i] = 0;
if(d[i+1] < 0) d[i+1] = 0;
if(d[i+2] < 0) d[i+2] = 0;
}
return pixels;
}
Это формула, которую вы ищете ...
var data = imageData.data;
if (contrast > 0) {
for(var i = 0; i < data.length; i += 4) {
data[i] += (255 - data[i]) * contrast / 255; // red
data[i + 1] += (255 - data[i + 1]) * contrast / 255; // green
data[i + 2] += (255 - data[i + 2]) * contrast / 255; // blue
}
} else if (contrast < 0) {
for (var i = 0; i < data.length; i += 4) {
data[i] += data[i] * (contrast) / 255; // red
data[i + 1] += data[i + 1] * (contrast) / 255; // green
data[i + 2] += data[i + 2] * (contrast) / 255; // blue
}
}
надеюсь, что это помогает!