我有2张图片im1
和im2
如下所示。 im2
图片与im1
相同,但它们之间的唯一区别是颜色。 im1
的每个颜色通道的RGB范围为(0-255,0-255,0-255),而im2
的RGB范围为(201-255,126-255,140-255)。我的练习是扭转添加的效果,这样我就可以尽可能地将im2
恢复到im1
。我有两个想法。第一种是匹配它们的直方图,因此它们都具有相同的颜色。我使用histeq
尝试了它,但它只恢复了图像的一部分。有没有办法将im2
的直方图更改为与im1
完全相同?第二种方法只是将每个像素值从im1
复制到im2
,但这是错误的,因为它没有恢复原始图像状态。有什么建议可以恢复图像吗?
答案 0 :(得分:4)
im2
转换为im1
需要线性权重组合。换句话说,给定一个RGB像素,其红色,绿色和蓝色分量被成形为来自损坏图像(im2
)的3 x 1向量,存在一些线性变换以在干净图像中获得其等效像素(im1
)。换句话说,我们有这种关系:
[R_im1] [R_im2]
[G_im1] = A * [G_im2]
[B_im1] [B_im2]
Y = A * X
在这种情况下, A
将是一个3 x 3矩阵。这基本上是执行矩阵乘法以获得输出校正像素。 im2
的输入RGB像素为X
,im1
的输出RGB像素为Y
。我们可以将此扩展到我们想要的任意数量的像素,其中来自im1
和im2
的像素对将在Y
和X
之间建立列。通常,这会将X
和Y
进一步扩展到3 x N
矩阵。要找到矩阵A
,您会发现最小均方误差解决方案。我不会进入它,但要找到A
的最佳矩阵,这需要找到伪逆。在我们的例子中,A
因此等于:
找到此矩阵A
后,您需要拍摄图像中的每个像素,对其进行整形以使其成为3 x 1向量,然后将此A
与此向量相乘以上。您可能会问自己的一件事是我需要从两个图像中获取哪种像素以使上述方法有效?您必须遵守的一条准则是,您需要确保从两张图像之间的相同的空间位置进行采样。因此,如果我们要在...说...第4行第9列抓取像素,则需要确保来自im1
和im2
的两个像素来自同一行,同一列,它们位于X
和Y
的相应相应列中。
使用这种方法的另一个小警告是,您需要确保在图像中采样大量像素以获得良好的解决方案,并且还需要确保采样的扩散在整个图像上。如果我们将采样本地化在一个小区域内,那么你就没有得到足够好的颜色分布,因此输出看起来不太好。您可以根据自己选择的问题选择多少像素,但从经验来看,您可以达到输出开始稳定的程度并且您没有看到任何差异。出于演示目的,我在整个图像中随机选择了2000像素。
因此,这就是代码的样子。我使用randperm
生成从1到M
的随机排列,其中M
是图像中的总像素数。这些生成线性索引,以便我们可以从图像中采样并构建我们的矩阵。然后,我们应用上面的等式来查找A
,然后取每个像素并应用A
的矩阵乘法来获得输出。没有进一步的麻烦:
close all;
clear all;
im1 = imread('http://i.stack.imgur.com/GtgHU.jpg');
im2 = imread('http://i.stack.imgur.com/wHW50.jpg');
rng(123); %// Set seed for reproducibility
num_colours = 2000;
ind = randperm(numel(im1) / size(im1,3), num_colours);
%// Grab colours from original image
red_out = im1(:,:,1);
green_out = im1(:,:,2);
blue_out = im1(:,:,3);
%// Grab colours from corrupted image
red_in = im2(:,:,1);
green_in = im2(:,:,2);
blue_in = im2(:,:,3);
%// Create 3 x N matrices
X = double([red_in(ind); green_in(ind); blue_in(ind)]);
Y = double([red_out(ind); green_out(ind); blue_out(ind)]);
%// Find A
A = Y*(X.')/(X*X.');
%// Cast im2 to double for precision
im2_double = double(im2);
%// Apply matrix multiplication
out = cast(reshape((A*reshape(permute(im2_double, [3 1 2]), 3, [])).', ...
[size(im2_double,1) size(im2_double,2), 3]), class(im2));
让我们慢慢浏览一下这段代码。我正在直接从StackOverflow读取您的图像。之后,我使用rng
设置种子,以便您可以在结束时重现相同的结果。设置种子很有用,因为它允许您重现我所做的随机像素选择。我们生成这些线性索引,然后为3 x N
和im1
创建im2
矩阵。查找A
正是我所描述的,但您可能不习惯rdivide
/ /
运算符。 rdivide
在运算符的右侧找到反转,然后将其与左侧的任何值相乘。这是一种更有效的计算方法,而不是分别计算右侧的倒数,然后在完成后再乘以左侧。事实上,MATLAB会给你一个警告,说明要避免单独计算逆,而你应该使用除法运算符。接下来,我将im2
转换为double
以确保精度,因为A
很可能是浮点值,然后通过A
的每个像素的乘法来计算结果。最后一行代码看起来非常令人生畏,但是如果你想弄清楚我是如何得出这个的,我用它来创建复古风格的照片,这也需要像这种方法一样的矩阵乘法,你可以在这里阅读:{{ 3}}。 out
存储我们的最终图像。运行此代码并显示out
的外观后,这就是我们得到的结果:
现在,输出看起来完全混乱,但颜色分布或多或少模仿输入原始图像的样子。我有几个解释为什么会这样的情况:
im2
中有多种颜色映射到im1
。如果im2
中有多种颜色映射到im1
,则矩阵A
的线性乘法不可能为{生成多种颜色在im1
中给出一个像素{1}}。相反,最小均方解决方案将尝试生成最小化误差的颜色,并为您提供最佳颜色。由于这个确切的原因,这可能会使图像的脸部和其他细节变得模糊不清。im2
并不完全干净。我还可以在所有频道中看到各种盐和胡椒噪音。这种方法的一个坏处是如果你的图像受到噪声的影响,那么这种方法就不能忠实地重建原始图像。您的图片仅会被错误的颜色映射损坏。如果引入任何其他类型的图像噪声,那么当您尝试基于噪声图像重建原始图像时,此方法肯定不会起作用。嘈杂的图像中有些像素从未出现在原始图像中,所以你没有运气让它恢复到以前的状态!如果你想看看原始图像和输出图像之间每个通道的直方图,我们得到的是:
我用来生成上图的代码是:
im2
左侧显示原始图像的红色,绿色和蓝色直方图,而右侧显示重建图像的相同直方图。您可以看到一般形状或多或少地模仿原始图像,但总体上存在一些峰值 - 很可能归因于量化噪声和两个图像的颜色之间的非唯一映射。
总而言之,这是我能做的最好的,但我认为这是练习的重点......表明它是不可能的。
有关如何执行色彩校正的更多信息,请查看How do I create vintage images in MATLAB?。这就是我的开始,并且可以在他的幻灯片中找到如何计算names = {'Red', 'Green', 'Blue'};
figure;
for idx = 1 : 3
subplot(3,2,2*idx - 1);
imhist(im1(:,:,idx));
title([names{idx} ': Image 1']);
end
for idx = 1 : 3
subplot(3,2,2*idx);
imhist(out(:,:,idx));
title([names{idx} ': Output']);
end
的推导。也许你可以在将来的工作中使用他所谈论的一些内容。
答案 1 :(得分:0)
您似乎需要使用缩放功能将im2
的值映射到im1
的值。
这非常简单,您可以编写一个缩放功能,以使其适用于任何此类情况。
基本缩放映射的工作方式如下:
out_value = min_output + (in_value - min_input) * (outrange / inrange)
假设输入值in_value
在值inrange=max_input-min_input
范围内,并且映射会在范围out_value
内生成输出值outrange=max_output-min_output
。我们还需要考虑最小输入和输出范围界限(min_input
和min_output
)以获得正确的映射。
例如,请参阅以下缩放功能代码:
%
% scale the values of a matrix using a set of limits
% possible ways to use:
% y = scale( x, in_range, out_range) --> ex. y = scale( x, [8 230], [0 255])
% y = scale( x, out_range) --> ex. y = scale( x, [0 1])
%
function y = scale( x, varargin );
if nargin<2,
error([upper(mfilename),':: Syntax: y=',mfilename,'(x[,in_range],out_range)']);
end;
if nargin==2,
inrange=[min(x(:)) max(x(:))]; % compute the limits of the input variable
outrange=varargin{1}; % get the output limits from the arguments
else
inrange=varargin{1}; % get the input limits from the arguments
outrange=varargin{2}; % get the output limits from the arguments
end;
if diff(inrange)==0, % row or column vector matrix or scalar
% just do a clipping...
if x>=outrange(2),
y=outrange(2);
elseif x<=outrange(1),
y=outrange(1);
else
y=x;
end;
else
% actually scale the data
% using: out = min_output + (x-min_input) * (outrange / inrange)
y = outrange(1) + (x-inrange(1))*abs(diff(outrange))/abs(diff(inrange));
end;
此函数获取值矩阵并将其缩放到所需范围
在您的情况下,它可以用作以下(变量img
是缩放的im2
):
for i=1:size(im1,3), % for each of the input/output image channels
output_range = [min(min(im1(:,:,i))) max(max(im1(:,:,i)))];
img(:,:,i) = scale( im2(:,:,i), output_range);
end;
这种方式im2
一次缩放到im1
个频道的值范围。输出变量img
应该是理想的变量。