HTML5 холст контраст изображения
Я пишу программу обработки изображений, которая применяет эффекты через обработку пикселей холста HTML5. Я достиг порогового значения, винтажа и цветовых манипуляций с пикселями, но невероятно, что я не могу изменить контраст изображения! Я пробовал несколько решений, но я всегда получаю слишком много яркости в изображении и меньше контрастного эффекта , и я не планирую использовать какие-либо библиотеки 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, что означает, что данные [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 ответов:
Более быстрый вариант (основанный на подходе Эшера ):
Вывод аналогичен нижеприведенному; эта версия математически та же, но работает гораздо быстрее.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) }
Примечания
Я решил использовать
contrast
диапазон+/- 100
вместо оригинал+/- 255
. Процентное значение кажется более интуитивным для пользователей или программистов, которые не понимают основных понятий. Кроме того, мое использование всегда привязано к элементам управления пользовательского интерфейса; диапазон от -100% до +100% позволяет мне помечать и связывать значение элемента управления напрямую, а не настраивать или объяснять его.Этот алгоритм не включает проверку диапазона, хотя вычисленные значения могут значительно превышать допустимый диапазон - это связано с тем, что массив, лежащий в основе Объект ImageData - это
Uint8ClampedArray
. как объясняет MSDN , с помощьюUint8ClampedArray
проверка диапазона обрабатывается для вас:" если вы указали значение, выходящее за пределы диапазона [0,255], то вместо него будет установлено значение 0 или 255."
Использование
Обратите внимание, что хотя лежащая в основе формула достаточно симметрична (допускает цикличность), данные теряются при высоких уровнях фильтрации, поскольку пиксели допускают только целочисленные значения. Например, к тому времени, когда вы де-насыщаете изображение до экстремальных уровней (>95% или около того), все пиксели в основном имеют однородный средний серый цвет (в пределах нескольких цифр от среднего возможного значения 128). Повторное увеличение контраста приводит к выравниванию цветовой гаммы.Кроме того, порядок операций важен при применении множественных регулировок контраста-насыщенные значения "выдуваются" (превышают зажатое максимальное значение 255) быстро, то есть сильно насыщают, а затем де-насыщение приведет к более темному изображению в целом. Де-насыщение и тогда насыщение, однако, не имеет такой большой потери данных, потому что значения подсветки и тени становятся приглушенными, а не обрезанными (см. объяснение ниже).
Вообще говоря, при применении нескольких фильтров лучше начинать каждую операцию с исходных данных и повторно применять каждую корректировку по очереди, а не пытаться отменить предыдущее изменение-по крайней мере, для качества изображения. Производительность скорость или другие требования могут диктовать по-разному для каждого ситуация.Пример Кода:
function contrastImage(imageData, contrast) { // contrast input as percent; range [-1..1] var data = imageData.data; // Note: original dataset modified directly! contrast *= 255; var factor = (contrast + 255) / (255.01 - contrast); //add .1 to avoid /0 error. 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; //optional (e.g. for filter function chaining) } $(document).ready(function(){ var ctxOrigMinus100 = document.getElementById('canvOrigMinus100').getContext("2d"); var ctxOrigMinus50 = document.getElementById('canvOrigMinus50').getContext("2d"); var ctxOrig = document.getElementById('canvOrig').getContext("2d"); var ctxOrigPlus50 = document.getElementById('canvOrigPlus50').getContext("2d"); var ctxOrigPlus100 = document.getElementById('canvOrigPlus100').getContext("2d"); var ctxRoundMinus90 = document.getElementById('canvRoundMinus90').getContext("2d"); var ctxRoundMinus50 = document.getElementById('canvRoundMinus50').getContext("2d"); var ctxRound0 = document.getElementById('canvRound0').getContext("2d"); var ctxRoundPlus50 = document.getElementById('canvRoundPlus50').getContext("2d"); var ctxRoundPlus90 = document.getElementById('canvRoundPlus90').getContext("2d"); var img = new Image(); img.onload = function() { //draw orig ctxOrig.drawImage(img, 0, 0, img.width, img.height, 0, 0, 100, 100); //100 = canvas width, height //reduce contrast var origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, -.98); ctxOrigMinus100.putImageData(origBits, 0, 0); var origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, -.5); ctxOrigMinus50.putImageData(origBits, 0, 0); // add contrast var origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, .5); ctxOrigPlus50.putImageData(origBits, 0, 0); var origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, .98); ctxOrigPlus100.putImageData(origBits, 0, 0); //round-trip, de-saturate first origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, -.98); contrastImage(origBits, .98); ctxRoundMinus90.putImageData(origBits, 0, 0); origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, -.5); contrastImage(origBits, .5); ctxRoundMinus50.putImageData(origBits, 0, 0); //do nothing 100 times origBits = ctxOrig.getImageData(0, 0, 100, 100); for(i=0;i<100;i++){ contrastImage(origBits, 0); } ctxRound0.putImageData(origBits, 0, 0); //round-trip, saturate first origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, .5); contrastImage(origBits, -.5); ctxRoundPlus50.putImageData(origBits, 0, 0); origBits = ctxOrig.getImageData(0, 0, 100, 100); contrastImage(origBits, .98); contrastImage(origBits, -.98); ctxRoundPlus90.putImageData(origBits, 0, 0); }; img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAADAFBMVEX0RydFRjuPweRoak+awuFzbknzUj9SV0T0RRVtb1lcak7zUTSUmX94hnOYoHFacmpCPS9zwvOBkG5Zbl7xVCCVxuhnfnV7fFOnoqU3OTFQZVNNXlCmxeBtfWlhaFlbXUmfyOVrh3/vXUiBfUcrKCF9wu50fXB4fGVlcGJIUEvtSDCUvd2Up4uJnYBUX0A7QzjtW1tYZUlqY0U1Miivy+BfdXJ/hWtuc2eNjWSCkWFbYVVhcU6Pl2tsf13xVUxyueeir4N+lICIkXdIUjuxr7JyfH1iemiHhFT5Rx+7zN2Xn2KCf11jeFl1jYhSZ11OTkGJxe6Kt9ybrp6fqZBkfoZ/in53jntBTUN4h2FPTDFFRy+GwOeju9iVp35dWj1uh25gZWqNmF9JWUX3SjOvxNtyjZlYeJdqrNeMn3F2dFmNj1KWkktFV1W7vbd/l4ydkIVOXWDgVEjwWzlqb3WXq6/Bp6OHlJuWn5aKmotSZ2t/jVWRgFKmkU92eUF+rdaKrNCnsqVnh5BqeEyHh0POmzxiZDuotbqkrW+lnmvEo1BafqZ3gYnmZWKonFyZjlvvZ1Xev0hlu+5aksKyr5RLaI+YkmrXZmqYnFa3l0tXVDZpXzGFoqWlkZa3hYHrbmbXuFGLo5Z4hU3UqkdhbUBvbThxoM29u6SVoaNVbXziXlRVVVRZndPNsl1/uOHCsbB5mJWSjZE9PyWWs9egpX+st36lm3eysnJ6bTdxkb5gcYfSWlMfGhxpwPdloLp8mqWCi4++s344SVKih0aYfT6JcTy/TzwsLzZAMR+drM2/kJGrnpBafntHYnk1PUNfVC3SUCpfr+XTc4OGeXnjaHSlqV+sgzm9mpviSTlRg7Rrh7HmdXbCdXDSeW5AVWy2pWHZa1ynt5Hge5DNiouMgIbGtGq6w86+ZFmkV06+jzxOQyXWiKKzcGeFosRgkajdrzqddHOEcFN6SCTRig2dRCbjRiK1ahXMnLV6VzuEXFyeaiy8voxgPh/CfBK6lK6ciHeFa2q3ecQCAAAjI0lEQVRo3iRVf2gbZRjOGUroaO5iTq8mx6WGxDTBpGGNl+baFJOGpXhtLs4mzY+RxjvMTJNQAw5tTuJsjnCiKAiTsNSBoVFGRWpsyJBuheps/oiNIhEEHQQFR9Hi2lFq14J+6sMH33HHve/7vD+eV9KO7Jav6nTVfL6cFuhiZYU/UzmTNCvNbkIbJ1B0HJ12E74FrRdDFxgkgfgwBlkKrG36tFoEO+dbY9ZaGGJktJtrmUu+hdmg3Y0Nmp2LdaVIizCvFFPF4YLkpFy9WtTpdOl0viRUK2yFpyrOsEW5uIChLVxrIUaMC1oMR7ksgjMkjobi4MIQn5pEsr6cOoRkGBJpBRKbl5Z9pGFBHjQHzcqusuFQsmFRGXZ0+GJeslu+kS+lBSFP06UqTYtjcYof01hmUR/mXcPOzA6ZvCTJQFaGUfs9fivHWT1qJhDKJsjcEo4wiDZHtgKB5cRy4pI2Lp8YIeTmcaUTnocbIlxX9vMiL6xI2rvtok6odPg0neJTYpVyOQthftZ4aTmhZRBjKIubEMiPk4jMpIayIavJ6gkxrcCty0ubawGEJAPLm8trr1z6Y2lzOeHjxs5qzEbY7l6P1XVUfV0s6XQFnpLsttt5oZRKpdOlEptiKZ6OVobOElo1ksNa5zASRWUQTiYCATK7xLSWSDLRWtq8fP7y8iVfC/rCt7m5mUgktCSJYAEUw0zucY1RA/dSsDNJTcVYlk8W8nlJu3yjmq/k2U4lla+wPM+OrRSkQ2rvKIliWhLL4iiSg0xMNrAUWP5jOcEgy3ubfyQubyImtTVE5pAsYwqZOEaN+9WQWnY1MkBopNL+nv6+usslOhsuuLAyB5xUK4U0O9kQOwIrUvmVWmT67JgfHYI4DpJZTR61TKaIKqDQKqkFfhJqda6VWMJQT8TjUaNciOM4k9/jiQIoIDUyKpMW5vqdFCvWO7Qz6ehJ83MUKHw1nU5R5XQJpKo0VChIr8qGIpGoAhx/TVFTNJvgKdpUQNcDJB7KqlW5AOOJAJsej4eDojarAoIUCigalXlqNQXkV1w1jivtDidP0XXluK5U5PslN/JzVSGv69AlgRallamiTCPzN8FvEUXTFm2qFDab4n/4FX5U4YnW/jtNGyCnCkUi5Rv7J7vWmoKDIBXgA/mNoL/6Vkou18T6A5eSoni2KImAOUkL777zDl2ZpHlW2a8ag0wyK/RvfCrV9m7z5KTZtAGoVAoVsKzGEc4D2WzDGky9bTw8PT48OP0BtelRz79sOJnf2CuPycMs7FDW63SHH6AKZUk1JayUUg6xmE/RIp3v6ZH6QUw4JAtZVfrt6uHp4f7h4fGOCYP0tppN4RnycB6PCpqfHx9SbcMHB8Pzxwenyh0NET+LAxCEN2iZmFY67esPlFLRORhzshWJcEMQ0pNsp0E3XC44SQ0MQP5VyGZVezirbefLw9PmHPBCbuPjMj1w8y+iNWi4J+y16vXJ04Pjk8PTU/mOafHDOMOgCBlfDbonYkkYnqAqLFunpAVpv6QslIr5fl2p02nAohPuGzSARuc8HEoYxzzbwunB4f3Dwx9MG3pjb6Rpa9aioOa1SFkaj25sbOA/HACcfpXTjy6a3STqzRmC8t6ZCSrspGjKOQ7TKTa/kpcIc8JwOiUUHWyn46Rpe2wwGMRQNOddtKj1evzwPyv3bBsKPwE19fpapNau1dq9YS4yHNHrZcfg62ROFcJmPoyToyjmm0XNFioZg9c7rJi0jHlA5XnJbaGtK5fSOoF2pkSXE7bPzwQXnkdaS2uzGpCQ0D7gsq/XN3cjmjNNcM8Vd6PtI/k095pOZbOBF03r9g7iXTcbRnCvL240PBeG+5LJcLI7E0vyNGx3FouS2zeAcqXSAtxolHSlyZTDbggaZjEExGQfUls39Bv/wX9WymmN+vbubi1yNHyVwCHOmLGGrDs7W6YtbTz4qtsoY2QGr1zrtiyau65uzNJ9QFN0QckDWbnd1glCzzudfLpRSoNKwQ4i2Of1erVo3DKNqq2ZzNZWJpNdWHh11qRoHp3st4+O7o14VEME2AAtJsNkEa07+GoQz6CjBEaMeONds8VZX5+BKTEZc5b6K5WKZHclBeQxVS9R9QbLlh7ABTgYXJz2xlHES/R64xyOqhnjyz65mtNbd3ua+6/t3/7Tx3hVGu3CWhDDEHnw1ZlFjUmLoASGy71yi91icXVnZrousyuZhGkHOySp6srptK6fFTssC3YZLFLzU4Q9KCfPkV4ZITXOEoOG4MjbWEBrQvRz905O/j4+/uvtpVyLw02ake9f+uyDx37tlWUx4EOuWZCfkxOxriVMwV1XjIVFsFAc/LRkF+wsXXVSpEG3daZ6qKTTPTGxOGLADFovbuUG7d/b7YOG0dVVBjGFRu7t37129/j26nIrlPnm4t7e3lsXv1i1qTItBGxLwjholGN9MaWSguszgEa9HoMtlKMgKbfTQk9HLDlZGoZddZ5PJgeDSoPh3PMEpsVDJqvfD5nuvHVxa1uVMRrunhxd++349wEDY3vrxfce/hffvfjW9RyDY5rxEa+bMLrNix++YVHOzFjqrgegv4Cyj0mK5XQqVUhVikJJl4cbIF8wOzY68rKBxI0hyLpz58qPP/64d+v8hT391tsv3W0eXTs+/P2vkNW298Sjjz7+6IXXby0FrmczS1oGnT6jwdxueczi+viN51wPzN1YLOkc7uFXwMQPT+VT9UYq1Uk56Tcd/evvw30yVGs0DHAmq/XOj9/dvAniffrTz/2m76/dP9n/4P63v/0C1RQfPf7UU0+evwWwd+VOxpRFNMSZabtl0R2LKWOuriVW78bqSWk4yVdA4XU6YbIkpNIdIPdAWeZ7+/ocBK5ZxVHT1pUrVy6++PATT1x49sIfR9Cfv9zfP7r2yLe/XbtXG3jomRfOn//0iYdv3rz54sVcNpdF1ZowEbbLqeQsDMOWJOARp8CknKlI2lVhuFzV0ZPJyR66KDbenbI7pwbtfpnMqIbufPLdd7euf3ThUYDH7kf+/Pnu/t1fHvn2659+L597/KnLTz70LHDy3i2MvP6NNYuMjstGCWPc7IorxfrMG10XHYu5YlSY/4cG841po47D+HG9E1oo0KPHOVbWa2vHtrYoZ1cL7dk/i3S1Zf2zpV5XuIaFWWCEhMboFKlQgy4GR9oMlk4lJQUdNg2Rzb+DJSaiKGJZZnQhLGwmakIk0YzgK43fW+K96Mv73PM83+d79yuC46Oui4Ov4+fe7r08aLVaKY/ZIzZ5hFX/3Jd96VQqFdsJ0BhJP7r819b48l/LG+W7xcmtv2+SOh1G+v2y2NmlpfX19NWrtfXixlKrydDtdEAadkfHdb1B393MNncjGYZ3Ua5zLnPvZcp1+RVPz2WzK5xhlC0nTlzKL72VWpRpCwnMSJPvFn/f3HrwYPmWXL67tfngJorRNAiMLcVsizLb0tWjp47WeYlDIUeUMHTb9d2Q/3VYXt3NXi8y8+mVDDOjqex89qJG1FvpabD2DjElT0mPHt1/J2Vbyi9qI5GAzmikfyoub8SLD36fjMvlc1vLAoSk6VjMlloEx+ATbETVFo1WtYaCQQPBGfT2jo57do6zdk9bEJwynxvVUJ9qBimP/ZNnPWaKp0zPSFtb9q+lF22FdMoWQTFdl5v+tlicOrJbPLIhL5Orx7/4AKVpiOVh8us7SzurL58dGZk/+05WyhkAc/LesN5ZYXeYfuxM5qAnYaV5CKfODc1Qva4fzQ2mSmmFVCo90NeXltkiibxWgLS7jd8Vt6bkxeLAuFwuV0/99iKKKYx0IKKVyVLppYJtafXl2v6q0Hy2/55er7effB9+r3cnX6ns5HIILqKGZnqtVPOFyxrrhbc9BGeyO021qoO1H76VAqtighIM/HqvOK6WF3fVW3GAjM29i2JGI4ZC8rLYrE0rW9859WLbTdX5tnmD3iC12+2Gjk5rs9XZ/EoyicyE8SszzJDGpTn36Qx18VXKYyXqDzrbDr38/FvrAAjEIgUU1SmM78nH1UeK8qkxgMSn5l5AhTLCeGltMWAA5GwbfA9ns47z54eHO5xWO1FpfbaZo5JEM8KLfEMMbh7kD0wM4i5PRUVDg/PaU4YDVZ/nYX5taCARoVEUbQfIlHp3V65Wg13lU3OvkUa3gqQFSAHUpPKnTp09O/9C9p0XgnrpyWBQX2m3VnSw3L3u6QzC4zyvwX24hhLjvS6R024iOnvYp1oO311Pr8siKJooYKggZTM+DhrK1YIS+dgXr9FuN0aDkkIBLIP52jlV2zbyzgc354PDQb1Tn9UbTuo7DHpv0jKNMJQGd/X09EAclMt87U1zM+Gsb2g5/OH30DAIhQwEMDSC6bDN+BPqsrL4FKAglIE/jMCg/SRaiAi1X4/FTq0ejar6g8HgS8Gs81qFw+AkOIKT5KZzCONjcCXP2M3UBGPydJorLrga8JaWQ4cvpbRpmRakBDAsgpLYpno8XrZbPr4Rl5eVqQdWIHYSihIJkAIkJkuNnIBTU1twPgtKCOt1R0OHk7Jy08dyAHFp+DBuxl24+NqFH03EhFnMtByUStbuLPohUZgvnU5BCpB4Ody9XD2uhqLEAaIQ2kgGUFKIRICsqhwhRygU0g9DFfVtQU5KcFxzstGCMHwpw2RwivLgLleltcHTA8mblL676UWtLQ9SSB2GPYSo1cehhrc3NgTIFytGYChoVIcJkHWbLLW0Cm2cVwHkZLb/ZIfD0MGBXdO5XCMiEvdQVCbDizW4xkU5r33SzZkqpJJ9dwFQeAiB0GkSxTbHAYKoNzaENsYnFxRQeQUWCJACJA9idvYfa82qVPP9Hef1Uj2UxaCXshyb81oQimGmcT4XHsKZixqJuaPC02B2Ur61S/nFRW3aFoE1D02E8doafwTcmtu+D6GAXe0KNIIaaR1Kk0JVtLLUal1Nq0qV7R/O9hv0wX4xUcmKuonmZNKC8JnqMP5DdRLHJ6hpymQ2TVhNImX1vnQ+pfUnYloSo4XNhbZvPR0vK0OeuH97ShivuXYIQwGvRlroYywC784RgLTWOvrrQw6DQRoNmaTRplwuyULjv2KSOMPnNAwvFlOueqmzouZQiUj5Jbyu/GRi1ibslHaArGyMlwNkbPv+2P8QzK3QoaQgZDbil9mqztTU1YWC8yrQ4Qi1OQzRqJezNLGsBRkc9IhEvDKsuXLY19MpxnsaKpW8WLmWT+W1fvSzAqrAsPZfjZGVsUegJkfG9rbVADk+0F6IKABCCpBYQutPxarOeOv62vqz2RBhCIWi99gDLOGVsBZLM/KvxzN4hQ9Xh8OEiBl9vfrDHoopaRGt5W0wXuhsDDWC8W6A/DL1CEBu7+2NwSzLAYIa3e0YDXbZZlHIfqmu7oTwR0mtNHveYSAIluCiFouXtUjqEaX4oEe5T3Slel9O5DtWKlaWKJWMr+TrtA22ij8xW1AABEJZGZsqFyDbe5CJAAnAl4QONjHpnw3QfmG4Hj/R6gxFVQSUJVoT5VggsF5vNMoifzItjJIrFZX68NFcY2mFSAknUabk6zuyfD7lRxMJhRFCAYhaXXYcOTJ2f+82QJDJdhQDiMJtpAsAIbW21b7n9u9nHdBGCJ4T7t+UlEjqJWy0FRnsMTOj35zO8NUlFvx0LgyeZapL9j12aTEPfpGJhO4hBH3jiUfKQMmtbbBLXo7MtaMK9wLmdrvpRIIkIwA5U7dfUuflvBIvwcLzWxq5psbG1qi3SYKIGkwNYj7D88pROKg9WVriOx328V/fuLtYuLNuI9GFQFeXTicoOQ5KIJPtsXIBsoAqumATGxWBBLil9ceunulTRSVNLEd0c1D1+lyp1wJXY2NjKfInZZ7o8TAininhw5mPfGLf6Wo4ud9Yk2ln87IIVFrRpQi4FSsDIAQZ2P5ne7IcQZBJgLTDq8xoTLymI7VaWX71cFVNTU2rlwWnWJMwuvWSRom3qUlSily4OPHxhJkwi0XKTObYR77SJ6vDvup9N26sywppGdwpAW7puowAgevnW//s7amPy8tuBbCuBUyhcOv+WKBJmXZ9qepMXU1TjbeVhadvkkgsJgvrrav3llqaGv/jwHpik4bDaJk4WDk4g3/SUsemXTIgaa1wUIgSU8VVCbMXm40GlTQjUQ5unoxIEyNxkIEZQcI8eFBJRDYTe5Aa9DC2EEyGkRDnFh0HMxJnOHhYdlvih03ogcvr+977Xvt7SCSVejpz+tLpE5cuHX31hQ28658wa7ogmY/+hBtAZJvNxoucmO2CGBq77bfPD+mRX7wNvEVwQIQY7IZXyCcIuGfIi1JnUOaoy8W8pLxeisKZII4j8VYkOZP6e/nK0dj+WGye7u+3a3QMTZZX5/xq3i9pFR5iUOa0P0dhXL2N3d1d8LJ1jScABF5cHcUxCMEV+iQEcd+RI0eGPM6zLhas6/KcBUKUiWFZJNlqzdyKQOtxO3X7wTjUG+x4v52GY+1m0W1LLxISKE9o4fd5dBRBphp7f/48mUamLvgdEvzJARHuMMjufo0JQdrk8RivH4P1Aym8Hg8KowpSLIwrksslHyffJ5Mzlx/aNWPYNolN0MBkc32O2AAQUeYkiRcdF3oRRH8u+2uveRwxgIM5UEoSOzaO64EUzoeDNI7jJhxHqQHm4UPUfI0xMsdQnJkPskglF2+13gPGzf0TdnsVwyYARQNMlta/LsohLSd2JNi6GlebhXlZs43GrEGPNAa1nAzbKMpSm7gKRD7RLEvjB2BVRsBcRqd5wMxCH4eijE43hvyOb7WSM8nUiVPz4/MUjWF2FhohAStX14tuPtTTlmS5DTdOnAUmO1ONxslRvXVvUCtFCZilLHE9kJDuSZqmBV/4wAGjN9j1FqiBukwowzA6igWQOFCJpO6vlEoPdOMkoJB26KDK1Uzabcu721xNromSLBENcNdOttHs1QMIfKRGJYLowA0Uic5t0lg47DOZfB78OgWh5WQYIMEAWAyW8cXC1lYrF6kkU6VXUGtgVZIkgQpZrmcyX/2dkE2So/DAco3YQ2BPprIFAyy+yIsdWUsoqsj5h4c3rn4KY3AGNgnGoSEPzhiDThdjHvGgb8w6iqECSByuXC7+rXLrypXtbQxSi6QtglCtVpcyxWGlyIu1PAxfUcQ1q1X/PdsoIGAyQhRVrdaRVyXC7U7zBxNL9fCdMBv0GeFiUA3j6dvHDDhHnAz1MkYhj3K5HMBElleelUr7Ndi7LgtauLdEVtcTcz3pRaWjKpwoyrCNO13hm9MGa0F0dFStg1A3HOAsdTGU2AzXl27cuTN0dwiKPRbSF3LSO4DqxnSBAPWfCWBUoOReKT0bM5FljBTqNGkK11fTabca6oGCVlQUuVaw6hFDtpk1GKxTNW4DpmWb7EDGp/Oh4urq5j0fLVhYk8l5EULRbKb6+vqO7WPMFBWIIS/+g3xbWVm+XXkwUSZ1pqowYhGwKgbH60QxOhni1Q1ZUWprs1ZQvNB8bpietrbFD7zNFv0QdbuLajSdTqzes9CQK+GgxWO5ex4UcbogHVGXOQaeRRYWFkCT5eVvlcitAPiwbtewFrzbDdXrmXR6eHIyqn7gCaX2o2BFkN7m7D+SzDW0aSiK45FuDWmdHQqVOZewKl1bUeNji0StD6gIWoVZHT6YhZmIUKXthhqEboUimhU2NIjPVqrRbDo/zA9LFNRaq1OjQ8ZoUyxSRSxTCzLQgQw8mSf5khDuj/85Nzc5/2szhl2umWEFfpFZvygO5btFIc+ywUDACUosnJ+mV9DEThPR4KDw9aBkxSoklUrFyhPl8kTm+ciVNbV1c5YufazP45VA4Xl+kyzIP5RIVffZS8CAL2PBCOlyfxhVlq2NSJosD/FD4FnwP1k1aFnZFrQ4BwkTzN4GDCNMWDtmajdht5BKJXa+HIuBmstXX8FX6+WaOueCbfVtL5emi8K9/AWoy7Aid49+DsOLjphJMhwOG83xL/LiKk0Z2nTwgsgKm1iWFbigBIY+mJ04Sq9AiUX4etxAGDDM0I6BkmQydv9+qhy7fPV9Lbjy9Ls5tGcv5QwW02peVFk+H2HFqgdxlxlxIW6StIEU48DMkUPDrBgRI7xYPJpXBZblnAEuUF8P2YKFhECbcZwgDAaiGjMYkGQyNcuBGfZidW1tHaxxFOagBtMBSSqqebA9eFEc/QxDQ7JcJEBmKR++VnUrP2RNkgX+gsBrqqpybW2BIGw/OGgCRWkcNcAMNuhRjaT6k8l+nXT5z8jCYzUe796Td691nbY70+mixAqsoF3//DGEdEBBYGwy7ja63Uazy0zODMuRiMyKAg8PSdFeH5Se4yyNzbB+0QS9mzbhuAHSBScCKmYxqYlzO0ZWH6hbAI1SK+w1+TnOJ0kqOx3K2WxmsxnZDgwjKHHrouBGx8DrUVHUNJ5VoxCq9MTCWRiv5+nefXYaXYWbCLQdpSFZGzEk2a8HQMqZkSXr59Sfbu2BhmxuU1s6rRYlfjobisdJUk+W2WZ0zzLgAniQtNANTeZ1jKCqrCQxTK/Vus3i8VJ2B1SGWIWaaKqJOoYh/f2ppA6JlV9kqq/sPrz14cMN0LxuqXuZVlmh81suFBqIFxIJN2l069nSC99h7EsUCok+W/i7rMkaz7OzISm+Zq/H6rFSvVt2OVAUCo877NCPIpVkSldSicWen6neX2OYd+7ZxT1dXTcb/c2KT5kecyGusVyoUJqcHI+T40Bxk26y9HuykIj3QWP/ndEUoPBy1BfkOOaJ1ep1dHrs/pM1KEGbCMzRUgOQ1H2oiA4pZ15U41jLOhDS09N6CncGBVX6lkO2u8amwBwqlCDGS+PjpUk4PmWzY2EbtKq5Lxqv/VR4tpNp9vn8bxth/7HBgVOUfxftaMApO/w7NiHA+C+lPDFye8ktbO7muW96jvec9tNOSfFn9aUkQWan/mb7CjC+DiglwPb6NZXN2Yw2s+sbw2qsIgV9fuYkWDi+aOPhww0Y7QCndBtFOQAyvwVJ6iWpVACSybxfsnD1vIeb79zZ03r2LtiXxUEd0mGzkWQWKCQJiEICGFOPBuIJss9s3I5ke6MMo0pSlOH8nRxzzXut64TXjm00DHb6rXaqqWbL8pZ/NZphaGplGMdPDG+te7ZiH9r50qI70G0sWIOmRckm5+C4MoI5R30oBNmHNdTZOZ5jaGC1To6T3uCg7V617HbZaTLciXU25od7RHcLhe5l3qaQQ2s6d2OQi0Z3Ldp6TtHjOaIg78//8zzv874+r0qBBAZcC7t/3lV1B7ZDAT5k9gsT/f3fvkPcKYMSCPiPQ/d+r125fOUzQFy7d2fos2tgl2ywSDbJmffmZ7a+emfr9tQUMTUD57VrsgM89lKPtY/o0KoNhi5ktfFfSH59AEH5RTUXykgbfMEsCjRJWvsPywMD+qH7P94f0p/c+xh+w1+79tnKiV6vhxS22S7Zxi83ua0pwjr1XpJMWsnozNrt22trdCJh0arVcHWo1SMGAyj5/JOboGMBlGzeCn0Q9HkDwSDGyALNWfshuy4B5D7MD/3JncjKFWhCARXe2cBgu+c65KasVoKYss5zSZKUo9FowmExWDTaPpLknobGmQUgmdWbUFN+/bfg3zDmcr4gbPcKOEtRouX7/tPDMpQR5YLvfhK5//NORGHAO5ABkCvpFmmdmrf209y8laZlwUJR/kQi4XFMcOq+Ptkx1mF4DiAgZfXzm4s3FxYePFyam9v27i6d2XGMlQUrIXNHrsu2yzYwvTIVISR6YCimt11ShBwVyS0iSVjnKfFCFKy04IGqBGnjsWjBptWesTGANFZByueri4q/tndzn875lhrLBSeGyaxfniBP02VlsVL8b0MGoAKDDf1XJMfHxwdACPfCPMmRVgKL1UlhDUoS1AszCt2Tp7XqsY4u6LGNIQ2gNFYbAHmwu5vdngv5MpnseaFQZAWWpeX6IWQrtDtsiDLmlWsDkFKXlbAr3tLrm1aOILa2OEqM4ZilXytQMJHNo6NXjSZNe7t2eqTLAp0DZKPRyABkQVGyu+vzBr2+LECceIxmWVYmxXgE0ngcGI+NlyMfIzblhU0x2OPtXdRFUiTnORErsiynfYGaZBh+tPObn64CBM5O1RbLtOc5JNPIZEANCFl48GDJp5Oyfy5JmQLuxATq4ICiBGczveLSK4Mj5UgZxlYMQfSXkAHXYZGUaY6VRfAVR7NUlGInzev8Rz8EOnWzIEXdMW0YG/MgGcUUyMJNWOezYV/uTy/v40EKVmeEgwNMxOJNCIzehuhdaRekFTBsegSW4HSrpa3TrIjZCy2RThKCwFKC32zmRwOBq4YujVarMYxZxhwO5CyT2cg0FhdXgbO7lPNmf8tJks9dcB5gWpllD/y4vxWP34mcuE4i6fSOawi5bHsb0uDHlXL8AisWRRxP4fbTOs3RMxTFTA6aJSgZXuPLXSOa2ZGOMaLLsYZkzvhMo1pdqsIOL1v1eXO5XCAg8RL4ixVEjLEf4H4RO4xXmvuRyE66XCvfH1gZiKy4SkfOIoYVcHveHcOK1nlGkEXGgK57e71hCVpnGo1GO93heBaigph5PrOxEQhWVxd3fcGsN5sN5SQ+WCg4CwyHsZj/4MBux+tYLKZc8aNKJd/cT1cqlRYGCZVKud2lFi7SXJKmorTADJrNowFUB6cjs0CZ7ph2PDs9oUBGR6VwAJQAwzfny+ay0vl5we8sOEWWZQpOv92d+hvH8BjcGBg8tZxYDMNL7pQ7FcdwcJkVahBBW4r+N9B1KdypM8wCY7YD9iWOiWkHYjYzvLQePqsuLYWDPigqWV/ouH4uOWO8hB3gnosiHnNX3PFUPv83DuNDBPA4IFL5UiqfKrkhMU6tgoWkpmgKvMW/rDMavaYRYLRr1ACZniBACWMeXR/8SKouhtBANRT05uY2T2Pn4RbP8347xtRxrHjgdldK+XQln4+XSuCfPfc+WMTddIMHL4rMqyT7hEwzfhQ1+zvDnZ1GowmUzLZ3EGqOcxAkIpgHzSgqrQer0BIOhEJeb/bY6TwOHRcKBdbPshQr4AVnCYLhSkf2Ins7f+xFajv7e5H9Wjzewi+KxboogrdkGSBAWb8aftloMqnaYZuihpNEYoIgkEl0chDtXJeCErre2RnS+ea2jefHwKryvB3HcZbFIOKxo2bFVXat1FZqOzVXrba/l95rHrqLuHhab6cpUpYTTJFB16/CwYKq22SabWvTal6Bo2r1UxxHIFSUYRhUCgRQs/kNVAoFje8aex8eb3vDGwEct4PhOISgcNS8U0una+VaJO3a29uvNA/jpzFMFE9BCksQFGowoC+HwzrdW8bu7tlu2Dtq2rXchJokAUJ7PDS4S1pnJoVBNByWjqHdva06vsj5AnZ+GSCAKcRKR0fNZtp1L11Jp9N7laP8Yct5Ua/j9WKdltlJVJhEdSYdnIyBEmgwmgAyotWSBEEokGhClsGV0JVmBAaF7zNiNKpUpuG/vAF+Y2N5GTg4GFaKV+IVmCXgtyYw4q14K3YRi4lFkfKzftSJ8uGQoVf1Yu8XqvdVbbPDj0LhUlthmbcmrUi0a/q2JzHICAkLnUAtI5Yuo2rYpHp415STspnl5WU7D1rckKz5eL4E+XUUz+f34TWkmZOJYX7MDlkYCIQ7vaHwrd7uL1Svtb/f1t72aJva8nRPsi/Z82oScTjW1jwO8BkdjXosFlBiUkHnfvPG8LZ3m8+cKRiwFMxteKTy7jxk8X7erUx1N++225cVtRu+oC7kfevWux9+qWprG36zrf2ZHm0HbFi0Pcnkq8jE2gwtOzy36SgV9chEAiDdw93Dqu2Hx5tStZGxL8MwcCscOwys0MAU5vK/drZ8luGrPshKb++tD69/ePf6h23XX+p5RtPToX2pJ9kDN/LCE1tPkU89BRtf5Q9UVrKv5/nHn3n8+ut3b7x53bTpDYfCqGLOf+3ceV5Xnv83NHyuC4fhM97NzVt3byhdzF++/u516JQ+Cn+xeTT56iNPwuORfwDmIxlqcXq9nQAAAABJRU5ErkJggg=="; });
canvas {width: 100px; height: 100px} div {text-align:center; width:120px; float:left}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div> <canvas id="canvOrigMinus100" width="100" height="100"></canvas> -98% </div> <div> <canvas id="canvOrigMinus50" width="100" height="100"></canvas> -50% </div> <div> <canvas id="canvOrig" width="100" height="100"></canvas> Original </div> <div> <canvas id="canvOrigPlus50" width="100" height="100"></canvas> +50% </div> <div> <canvas id="canvOrigPlus100" width="100" height="100"></canvas> +98% </div> <hr/> <div style="clear:left"> <canvas id="canvRoundMinus90" width="100" height="100"></canvas> Round-trip <br/> (-98%, +98%) </div> <div> <canvas id="canvRoundMinus50" width="100" height="100"></canvas> Round-trip <br/> (-50%, +50%) </div> <div> <canvas id="canvRound0" width="100" height="100"></canvas> Round-trip <br/> (0% 100x) </div> <div> <canvas id="canvRoundPlus50" width="100" height="100"></canvas> Round-trip <br/> (+50%, -50%) </div> <div> <canvas id="canvRoundPlus90" width="100" height="100"></canvas> Round-trip <br/> (+98%, -98%) </div>
Объяснение
(отказ от ответственности-я не специалист по изображениям и не математик. Я пытаюсь дать разумное объяснение с минимальными техническими деталями. Некоторые махания руками ниже, например 255=256, чтобы избежать проблем с индексацией, и 127,5=128, для упрощения чисел.)
Так как для данного пикселя возможное число ненулевые значения для цветового канала-255 , "неконтрастное", среднее значение пикселя-128 (или 127, или 127,5, если хотите поспорить, но разница незначительна). Для целей этого объяснения величина"контраста" - это расстояние от текущего значения до среднего значения (128) . Настройка контраста означает увеличение или уменьшение разницы между текущим значением и средним значением.
Задача, которую решает алгоритм, состоит в том, чтобы:
- выбрал постоянный коэффициент для регулировки контраста по
- для каждого цветового канала каждого пикселя масштабируйте "контраст" (расстояние от среднего) на этот постоянный коэффициент
Или, как намекается в CSS spec, просто выбирая наклон и перехват линии:
<feFuncR type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5"/>
Обратите внимание на термин
type='linear'
; мы выполняем линейную настройку контраста в цветовом пространстве RGB , в отличие от квадратичной функции масштабирования, регулировка на основе яркости илисогласование гистограммы .Если вы помните из класса геометрии, формула для линии
Для наших целей этот график представляет входное значение (ось x) и результат (ось y). Мы уже знаем, чтоy=mx+b
.y
- это конечное значение, которое мы ищем, наклонm
- это контраст (илиfactor
),x
является начальным значением пикселя, аb
- перехватом оси y (x=0), которая сдвигает линию по вертикали. Напомним также, что поскольку y-перехват не находится в начале координат (0,0), то формула также может быть представлена в видеy=m(x-a)+b
, где именноa
- смещение x, смещающее линию по горизонтали.b
, y-Перехват (дляm=0
, без контраста) должен быть 128 (что мы можем проверить против 0.5 из спецификации - 0.5 * полный диапазон 256 = 128).x
-это наше исходное значение, поэтому все, что нам нужно, это вычислить наклонm
и смещение xa
.Во-первых, склон
m
- это "подъем над пробегом", или(y2-y1)/(x2-x1)
- поэтому нам нужны 2 точки, которые, как известно, находятся на нужной линии. Нахождение этих точек требует объединения нескольких вещей:Принимая все это вместе, мы можем сделать вывод, что независимо от применяемого контраста (наклона) наша результирующая линия будет центрирована на (и вращаться вокруг)
- наша функция принимает форму графа перехвата линий
- y-перехват находится на
b = 128
- независимо от наклона (контраста).- максимальное ожидаемое значение ' y ' равно 255, а минимальное-0
- диапазон возможных значений ' x ' составляет 256
- нейтральное значение всегда должно оставаться нейтральным: 128 => 128 независимо от наклона
- регулировка контраста
0
не должна приводить к изменению между входом и выходом; то есть наклон 1:1.128,128
. Поскольку наш y-перехват ненулевой, x-перехват также ненулевой; мы знаем, что X-диапазон имеет ширину 256 и центрирован в середине, поэтому он должен быть смещен на половину возможного диапазон: 256/2 = 128. Итак, теперь дляy=m(x-a)+b
мы знаем все, кромеm
. Вспомним еще два важных момента из класса геометрии:
- линии имеют одинаковый наклон, даже если их расположение меняется; то есть
m
остается неизменным независимо от значенийa
иb
.- наклон прямой можно найти, используя любые 2 точки На линии
Чтобы упростить обсуждение наклона, давайте переместим начало координат к x-перехвату (-128) и игнорировать
a
иb
на мгновение. Наша исходная линия теперь повернется через (0,0), и мы знаем, что вторая точка на линии лежит далеко от полного диапазона какx
(вход), так иy
(выход) в (255,255).Мы позволим новой линии развернуться на (0,0), поэтому мы можем использовать ее как одну из точек на новой линии, которая будет следовать за нашим конечным контрастным наклоном
m
. Вторая точка может быть определена путем перемещения текущего конца в (255,255) на некоторую величину; так как мы ограниченная одним входом (contrast
) и использующая линейную функцию, эта вторая точка будет одинаково перемещаться в направленияхx
иy
на нашем графике.Координаты (x,y) 4 возможных новых точек будут
255 +/- contrast
. Поскольку увеличение или уменьшение x и y удержит нас на исходной линии 1: 1, давайте просто посмотрим на+x, -y
и-x, +y
, как показано.Более крутая линия (-x, +y) связана с положительным
contrast
регулированием; это (x, y) координаты такие (255 - contrast
,255 + contrast
). Координаты более мелкой линии (отрицательныеcontrast
) находятся таким же образом. Обратите внимание, что самое большое значимое значениеcontrast
будет 255 - самое большее, что начальная точка (255,255) может быть переведена до получения вертикальной линии (полный контраст, все черное или белое) или горизонтальной линии (нет контраста, все серое).Итак, теперь у нас есть координаты двух точек на нашей новой прямой - (0,0) и (
255 - contrast
,255 + contrast
). Мы подключаем это к уравнение наклона, а затем подключите его к уравнению полной линии, используя все части из предыдущего:
y = m(x-a) + b
m
=(y2-y1)/(x2-x1)
=>((255 + contrast) - 0)/((255 - contrast) - 0)
=>(255 + contrast)/(255 - contrast)
a = 128
b = 128
y = (255 + contrast)/(255 - contrast) * (x - 128) + 128
QEDМатематик заметит, что результирующее
m
илиfactor
является скалярным (безразмерным) значением; вы можете использовать любой диапазон, который вы хотите дляcontrast
, если он соответствует константе (255
) вfactor
расчет. Например, диапазонcontrast
+/-100
иfactor = (100 + contrast)/(100.01 - contrast)
, который я действительно использую, чтобы исключить шаг масштабирования до 255; я просто оставил255
в коде вверху, чтобы упростить объяснение.
Примечание о "магии" 259
Исходная статья использует "магию" 259, хотя автор признает, что не помнит, почему:
259 действительно должно быть 255 или, возможно, 256-число возможных ненулевых значений для каждого канала каждого пикселя. Обратите внимание, что в исходном вычислении" я не могу вспомнить, рассчитал ли я это сам, или я прочитал это в книге или онлайн.".
factor
259/255 отменяет - технически 1.01, но конечные значения являются целыми числами, поэтому 1 для всех практических целей. Так что этот внешний термин можно отбросить. На самом деле использование 255 для константы в знаменателе, однако, вводит возможность деления на нулевую ошибку в Формуле; корректировка немного большее значение (скажем, 259) позволяет избежать этой проблемы, не внося существенных ошибок в результаты. Я решил использовать 255.01 вместо этого, так как ошибка ниже, и это (надеюсь) кажется менее "волшебным" новичку.Насколько я могу судить, не имеет большого значения, какие вы используете - вы получаете одинаковые значения, за исключением незначительных симметричных различий в узкой полосе низких значений контраста с низким положительным увеличением контраста. Мне было бы любопытно обойти обе версии неоднократно и сравнить с исходными данными, но этот ответ уже занял слишком много времени. :)
После попытки ответа Шахрияра Саффаршарга, он вел себя не так, как должен был бы вести себя контраст. Я наконец-то наткнулся на этот алгоритм, и он работает как заклинание!
Для получения дополнительной информации об алгоритме прочитайтеЭту статью и раздел комментариев.
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 меньше моей половины 255, которая равна 127, поэтому я считаю пиксель ближе к [0,0,0], поэтому если я хочу минус 10 контраста, то я забираю 10 от каждого цвета в среднем Таким образом, я должен разделить значение каждого цвета на среднее значение итога, которое было 115 для этого случая, и умножить его на мой контраст и вычеркнуть конечное значение из этого конкретного цвета:
Например, я возьму 105 (красный) из моего пикселя, поэтому я делю его на общее среднее значение 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" к окну canvas, я считаю, что вам нужно на самом деле сопоставить массив, который imageData возвращает в массив RGB LUT.
(от иллюзии света) В качестве примера начало 1D LUT может выглядеть примерно так: Примечание: строго говоря, это 3x 1D LUTs, так как каждый цвет (R, G, B) является 1D LUT
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
Что странно, но вы видите, что для заданного значения 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 } }
Надеюсь, это поможет!