【发布时间】:2015-08-12 06:09:36
【问题描述】:
我正在使用从 stackoverflow 获得的一段代码用于 floyd steinberg 抖动算法。
如下。但它没有像预期的那样正确抖动图像。有没有人对此有正确的实现,或者任何人都可以更正下面的代码。实际需求是将24 bit彩色图像转换为1 bit灰度抖动图像。
我认为下面的 floyd steinberg 方法部分是正确的,但是在调用该方法之前调用了一些我不知道的函数。我对 opencv 很陌生。它是针对 ios 项目的。
-(UIImage*)processImage:(UIImage*)chosenImage//ios
{
int nrColors = 8;
cv::Mat img;
UIImageToMat(chosenImage, img);
// i am not sure of this part--->
cv::Mat colVec = img.reshape(1, img.rows*img.cols); // change to a Nx3 column vector
cv::Mat colVecD;
colVec.convertTo(colVecD, CV_32FC3, 1.0); // convert to floating point
cv::Mat labels, centers;
cv::kmeans(colVecD, nrColors, labels,
cv::TermCriteria(CV_TERMCRIT_ITER, 100, 0.1),
3, cv::KMEANS_PP_CENTERS, centers); // compute k mean centers
// replace pixels by there corresponding image centers
cv::Mat imgPosterized = img.clone();
for(int i = 0; i < img.rows; i++ )
{
for(int j = 0; j < img.cols; j++ )
{
for(int k = 0; k < 3; k++)
{
imgPosterized.at<cv::Vec3b>(i,j)[k] = centers.at<float>(labels.at<int>(j+img.cols*i),k);
}
}
}
//<---- i am not sure of this part
// convert palette back to uchar
cv::Mat palette;
centers.convertTo(palette,CV_8UC3,1.0);
img= floydSteinberg(img,palette);
cv::Mat imgGray;
//cvtColor(img, imgGray,cv::COLOR_RGBA2GRAY);
chosenImage= MatToUIImage(img);
return chosenImage;
}
//floyd steinberg algorithm
cv::Mat floydSteinberg(cv::Mat imgOrig, cv::Mat palette)
{
cv::Mat img = imgOrig.clone();
cv::Mat resImg = img.clone();
for(int i = 0; i < img.rows; i++ )
{
for(int j = 0; j < img.cols; j++ )
{
cv::Vec3b newpixel = findClosestPaletteColor(img.at<cv::Vec3b>(i,j), palette);
resImg.at<cv::Vec3b>(i,j) = newpixel;
for(int k=0;k<3;k++)
{
int quant_error = (int)img.at<cv::Vec3b>(i,j)[k] - newpixel[k];
if(i+1<img.rows)
img.at<cv::Vec3b>(i+1,j)[k] = fmin(255,fmax(0,(int)img.at<cv::Vec3b>(i+1,j)[k] + (7 * quant_error) / 16));
if(i-1 > 0 && j+1 < img.cols)
img.at<cv::Vec3b>(i-1,j+1)[k] = fmin(255,fmax(0,(int)img.at<cv::Vec3b>(i-1,j+1)[k] + (3 * quant_error) / 16));
if(j+1 < img.cols)
img.at<cv::Vec3b>(i,j+1)[k] = fmin(255,fmax(0,(int)img.at<cv::Vec3b>(i,j+1)[k] + (5 * quant_error) / 16));
if(i+1 < img.rows && j+1 < img.cols)
img.at<cv::Vec3b>(i+1,j+1)[k] = fmin(255,fmax(0,(int)img.at<cv::Vec3b>(i+1,j+1)[k] + (1 * quant_error) / 16));
}
}
}
return resImg;
}
float vec3bDist(cv::Vec3b a, cv::Vec3b b)
{
return sqrt( pow((float)a[0]-b[0],2) + pow((float)a[1]-b[1],2) + pow((float)a[2]-b[2],2) );
}
cv::Vec3b findClosestPaletteColor(cv::Vec3b color, cv::Mat palette)
{
int i=0;
int minI = 0;
cv::Vec3b diff = color - palette.at<cv::Vec3b>(0);
float minDistance = vec3bDist(color, palette.at<cv::Vec3b>(0));
for (int i=0;i<palette.rows;i++)
{
float distance = vec3bDist(color, palette.at<cv::Vec3b>(i));
if (distance < minDistance)
{
minDistance = distance;
minI = i;
}
}
return palette.at<cv::Vec3b>(minI);
}
【问题讨论】:
-
问题是
palette,目前是8位。所以你的算法只是将它从 24 位转换为 8 位 -
此代码不是为了抖动到黑白,而是为了优化调色板。而且效率极低。
-
@YvesDaoust 我添加了 cvtColor 以将优化的调色板转换为灰度。您有使用 floyd steinberg 算法将 24 位彩色图像转换为灰色的实现吗?
-
@bro 不仅如您在图像中看到的那样,它不是正确抖动的图像。它应该包含点而不是线。并且它的部分转换仅如您在图像中看到的那样,右侧未转换
-
使用 (R+G+B)/3 作为亮度,而不是 3 个单独的通道。将此平均值与 128 进行比较,以决定黑色 (0) 或白色 (255)。使用该值计算量化误差。根据像素格式分配目标图像 (0, 0, 0)/(255, 255, 255) 或 0/255 或 0/1。