我正在尝试为程序编写一种算法,以在图像上绘制均匀的垂直渐变。 即我想沿着图像的 m 行将像素颜色从 0 更改为 255,但找不到一个好的通用算法来做到这一点。
我尝试使用 opencv 实现这样的东西,但它似乎不起作用
#include <opencv2/opencv.hpp>
int main(){
//Make the image white.
cv::Mat Img(w_height, w_width, CV_8U);
for (int y = 0; y < Img.rows; y += 1) {
for (int x = 0; x < Img.cols; x++) {
Img.at<uchar>(y, x) = 255;//White
}
}
// try to create an even, vertical gradient
for(int row = 0; row < Img.rows; row++ ){
for(int col = 0; col < Img.cols; col++){
Img.at<uchar>(row, col) = col % 256;
}
}
cv::imshow("Window", Img);
cv::waitKey(0);
return 0;
}
解决这个问题需要了解三个简单的技巧:
1. 插值:
从一个值逐渐变为另一个值的过程称为插值。有多种插值颜色值的方法:最简单的一种是线性插值每个分量,即以以下形式:
interpolated = start * (1-t) + dest * t
.
哪里
-
start
是向值dest
进行插值的值。 -
t
表示插值应与目标值的接近程度,dest
0
到1
的刻度上,0
是纯start
色,1
是纯dest
色。
您会发现 RGB 颜色空间中的线性插值不会生成自然的颜色路径。作为高级步骤,您可以改用 HSV 颜色空间。有关颜色插值的更多信息,请参阅此问题。
2. 离散化:
不幸的是,插值会产生实数。因此,我们必须将它们离散化,以便能够将它们用作整数颜色值。最好的方法是使用 round()
C++。
3. 求插值点:
现在,我们只需要在图像的每一行t
一个实值插值点。我们可以通过分析我们希望看到的输出来推断出一个公式:
- 对于最底部的行(第 1 行),我们希望有
t == 0
,因为这是我们希望纯起始颜色出现的地方。 - 对于最上面的行(第 m 行),我们希望有
t == 1
因为这是我们希望纯目标颜色出现的地方。 - 对于每隔一行,我们希望
t
与到最底部行的距离线性缩放。
实现此结果的公式为:
t = rowIndex / m
通过适当地改变这个公式,该方法可以很容易地适应其他梯度方向。
示例代码(使用线性插值,C++):
#include <algorithm>
#include <cmath>
Color interpolateRGB(Color from, Color to, float t)
{
// Clamp __t__ to range [0,1]
t = std::max(std::min(0.f, t), 1.f);
// Interpolate each RGB component
uint8_t r = std::roundf(from.r * (1-t) + to.r * t);
uint8_t g = std::roundf(from.g * (1-t) + to.g * t);
uint8_t b = std::roundf(from.b * (1-t) + to.b * t);
return Color(r, g, b);
}
void fillWithGradient(Image& img, Color from, Color to)
{
for(size_t row = 0; row < img.numRows(); ++row)
{
Color value = interpolateRGB(from, to, row / (img.numRows()-1));
// Set all pixels of this row to __value__
for(size_t col = 0; col < img.numCols(); ++col)
{
img.setPixel(row, col, value);
}
}
}
基本思想是使用n/(m-1)
的除法r
的其余部分,并在每次迭代时将其添加到n
:
#include <iostream>
#include <vector>
using namespace std;
vector<int> gradient( int n, int m ) {
div_t q { 0, 0 };
vector<int> grad(m);
for( int i=1 ; i<m ; ++i ) {
q = div( n + q.rem, m-1 );
grad[i] = grad[i-1] + q.quot;
}
return grad;
}
int main() {
for( int i : gradient(255,10) ) cout << i << ' ';
cout << 'n';
return 0;
}
输出:
0 28 56 85 113 141 170 198 226 255