还原过滤器 invert() CSS 规则

Revert a filter invert() CSS rule

我有一整段代码,其中应用了 CSS 规则 filter: invert(0.85)

在这个块里面,我有一个图像,当然也遵循这个CSS规则,并且是倒置的。

我需要为所述图像还原此 invert()

可能吗?我试了invert(1),但是图像和之前不太一样,还是有点倒(因为第一个invert只有0.85,没有1)

看这个例子:

body{
  font-size: 0;
}

div{
  display: inline-block;
  width: 50%;
}

.filter{
  filter: invert(0.85);
}

.filter img{
  filter: invert(1);
}
<div class="initial">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>
<div class="filter">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>

您需要结合使用其他 CSS filter() 来还原更改。根据最初应用的 filter(),这可能非常困难。

在这种特殊情况下,只有 invert(0.85) 被应用,我们可以尝试其他几个 filter() 来使图像恢复正常。

通过一些实验,以下方法似乎有效:

filter: invert(1) contrast(1.3) saturate(1.4);

这是一个粗略的猜测,但从视觉上看似乎可以解决问题。

.flex {
  width: 100%;
  display: flex;
}

img {
  width: 100%;
  display: block;
}

.flex > div{
  flex: 1;
}

.filter {
  filter: invert(0.85);
}

.filter img {
  filter: invert(1) contrast(1.3) saturate(1.4);
}

p {
  color: red;
}
<div class="flex">
  <div class="initial">
    <p>Some red text</p>
    <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
  </div>
  <div class="filter">
    <p>Some red text</p>
    <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
  </div>
</div>

TL;DR

您无法通过应用另一个 invert() 或其他过滤器的组合来还原 invert() 过滤器。


首先,我将从一个基本示例和解释开始:invert() 滤镜用于通过指定反转百分比(或 0 中的值)来反转颜色1)。如果我们使用值 1,我们会完全反转颜色,因此很容易回到初始状态,因为我们只需再次应用相同的反转:

.container {
 display:flex;
 justify-content:space-around;
}

.inner {
  height: 200px;
  width: 200px;
  background: 
  linear-gradient(to right, rgb(255,0,0) 50%, rgb(0,255,255) 0) 0 0/100% 50% no-repeat, 
  
  linear-gradient(to right, rgb(50,0,60) 50%, rgb(205,255,195) 0) 0 100%/100% 50% no-repeat;
}
<div class="container" style="filter:invert(1)">
  <div class="inner"></div>
  <div class="inner" style="filter:invert(1)"></div>
</div>

从这个例子中我们也可以理解反转是如何处理颜色的。我们只需对 RGB.

的每个值执行 (255 - x)

现在让我们考虑另一个值的反转,让我们取 0.85:

.container {
  display: inline-block;
}

.inner {
  display: inline-block;
  height: 200px;
  width: 200px;
  background: linear-gradient(to right, rgb(255, 0, 0) 50%, rgb(0, 255, 255) 0) 0 0/100% 50% no-repeat, linear-gradient(to right, rgb(50, 0, 60) 50%, rgb(205, 255, 195) 0) 0 100%/100% 50% no-repeat;
}
<div class="container" style="filter:invert(0.85)">
  <div class="inner"></div>
</div>
<div class="inner"></div>

它是如何工作的?

对于第一种颜色 (rgb(255,0,0)) 我们得到这个 (rgb(38, 217, 217)) 所以计算如下:

255 - [255*(1-0.85) + x*(2*0.85-1)]

所以我们的目标是反转这个公式:

f(x) = 255 - [255*(1-p) + x*(2*p-1)] , p a value in the range [0,1]

您可能会清楚地注意到,当 p=0 时,我们将拥有 f(x)=x,而当 p=1 时,我们将拥有 f(x)=255-x。现在让我们用f(x) 来表示x的值(我这里用y

x = 1/(2*p-1) * [255 - (255*(1-p) +y)]

让我们试着让它类似于一个反转函数。让我们有y=(2*p-1)*y' 我们将获得:

x = 1/(2*p-1) * [255 - (255*(1-p) +(2*p-1)*y')]

相当于

x = 1/(2*p-1) * f(y') ---> x = K * f(K*y) with K = 1/(2*p-1)

这里f是使用p相同值的反函数,K是根据值p计算的常数。我们可以区分3种情况:

  1. p的值等于0.5时函数没有定义,因此invert不能被反转(你可以顺便尝试应用invert( 0.5) 看结果)
  2. 当p的值大于0.5时,K是值,范围在[1,+infinity].
  3. 当p的值小于0.5时,K是值,范围[-infinity,-1].

所以如果我们省略第一种情况,K可以这样设置K=1/K'其中K'是[-1,1]/{0}范围内的值,abs(K')是在 ]0,1] 的范围内。那么我们的函数可以这样写:

x = (1/K') * f(y/K') where K' in the range [-1,1] define by (2*p - 1)

在这一点上,我们用一个 invert 函数和一个 multiplication/division 来表达我们的函数,其值我们可以很容易地计算出来。

现在我们需要找到哪个过滤器应用了 multiplication/division。我知道有 brightnesscontrast 过滤器使用线性变换。

如果我们使用brightness(p),论坛如下:

f(x) = x*p;

如果我们使用 contrast(p),公式将如下所示:

f(x) = p*(x - 255/2) + 255/2

这将在这里结束...

正如最初所说,我们无法使用其他过滤器恢复 invert()。对于某些值,我们可能可以对此进行近似,但对于其他值则不可能(例如 0.5)。换句话说,当应用 invert() 时,我们会丢失一些无法取回的信息。这就像,例如,当您将彩色图像转换为黑白版本时。您无法恢复初始颜色。

更新

这里有一些JS代码来证明上面的计算是正确的,看看结果。我使用 canvas 以便在应用我的函数时再次绘制图像:

不幸的是,出于安全和跨浏览器来源问题,我无法在代码段中使用图像,所以我考虑了渐变

var canvas = document.querySelector('canvas');
var img = document.querySelector('img');
var ctx = canvas.getContext('2d');
//we draw the same image on the canvas (here I will draw the same gradient )
//canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var grd = ctx.createLinearGradient(0, 0, 150, 0);
grd.addColorStop(0, "blue");
grd.addColorStop(1, "red");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 150, 150);

//we get the data of the image
var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 150);
var pix = imgData.data;
var p = 0.85;
// Loop over each pixel and apply the function
for (var i = 0, n = pix.length; i < n; i += 4) {
  pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
  pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
  pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
  // i+3 is alpha (the fourth element)
}
//Draw the image again
imgData.data = pix;
canvas.getContext('2d').putImageData(imgData, 0, 0);
body {
  font-size: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 150px;
  width: 150px;
  background: linear-gradient(to right, blue, red);
}

.filter {
  filter: invert(0.85);
}
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas></canvas>
</div>

正如我们所见,我们有 3 张图像:原始图像反转图像以及我们应用函数的图像让它变回原来的样子。

我们在这里取得了不错的结果,因为该值接近 1。如果我们使用另一个更接近 0.5 的值,结果会很糟糕,因为我们接近函数定义位置的限制:

var canvas = document.querySelector('canvas');
var img = document.querySelector('img');
var ctx = canvas.getContext('2d');
//we draw the same image on the canvas (here I will draw the same gradient )
//canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var grd = ctx.createLinearGradient(0, 0, 150, 0);
grd.addColorStop(0, "blue");
grd.addColorStop(1, "red");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 150, 150);

//we get the data of the image
var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 150);
var pix = imgData.data;
var p = 0.6;
// Loop over each pixel and apply the function
for (var i = 0, n = pix.length; i < n; i += 4) {
  pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
  pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
  pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
  // i+3 is alpha (the fourth element)
}
//Draw the image again
imgData.data = pix;
canvas.getContext('2d').putImageData(imgData, 0, 0);
body {
  font-size: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 150px;
  width: 150px;
  background: linear-gradient(to right, blue, red);
}

.filter {
  filter: invert(0.6);
}
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas></canvas>
</div>

您可以使用此代码创建一个通用函数,允许您部分还原invert()过滤器:

  1. 使用一些 JS,您可以轻松找到过滤器中使用的 p 的值。
  2. 您可以使用特定的选择器仅将特定的图像定位到要应用此逻辑的位置。
  3. 如您所见,我创建了一个 canvas,所以我的想法是创建它然后隐藏图像以仅保留 canvas。

这是一个更具交互性的演示:

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var init = function() {
  var grd = ctx.createLinearGradient(0, 0, 150, 0);
  grd.addColorStop(0, "blue");
  grd.addColorStop(1, "red");
  ctx.fillStyle = grd;
  ctx.fillRect(0, 0, 150, 100);
  var grd2 = ctx.createLinearGradient(0, 0, 0, 150);
  grd2.addColorStop(0, "green");
  grd2.addColorStop(1, "yellow");
  ctx.fillStyle = grd2;
  ctx.fillRect(40, 0, 70, 100);
}
var invert = function(p) {
  //we get the data of the image
  var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 100);
  var pix = imgData.data;
  // Loop over each pixel and apply the function
  for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
    pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
    pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
    // i+3 is alpha (the fourth element)
  }
  //Draw the image again
  imgData.data = pix;
  canvas.getContext('2d').putImageData(imgData, 0, 0);
}
init();
$('input').change(function() {
  var v = $(this).val();
  $('.filter').css('filter', 'invert(' + v + ')');
  init();
  invert(v);
})
p {
  margin: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 100px;
  width: 150px;
  background: linear-gradient(to bottom, green, yellow)40px 0/70px 100% no-repeat, linear-gradient(to right, blue, red);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas height="100" width="150"></canvas>
</div>
<p>Adjust the value of the invert filter</p>
<input type="range" value="0" min=0 max=1 step=0.05>

从这个演示中我们还可以注意到,当我们接近值 0.5 时,我们没有得到好的结果,因为过滤器不能在 0.5 处反转,如果我们参考根据之前的计算,K的值变得很大,因此K'太小了。

其他答案表明 CSS 很难做到这一点。这是您可能会发现有用的基本 javascript 方法。您检测 .filter 是否包含图像,如果包含,则删除 class filter.

var filteredDiv = document.querySelectorAll(".filter"),
  len = filteredDiv.length,
  i;

for (i = 0; i < len; i++) {
  if (filteredDiv[i].getElementsByTagName('img').length > 0) {
    filteredDiv[i].classList.remove('filter');
  }
}
body {
  font-size: 0;
}

.filter {
  filter: invert(.85);
}

div {
  display: inline-block;
  width: 200px;
}

img {
  width: 100%;
  height: auto;
}

div>div {
  background: yellow;
  font-size: 2rem;
}
<div class="filter">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>
<div class="filter">
  <div>Sample text</div>
</div>