在opencv中执行floyd steinberg抖动algorithm无法正常工作

我正在使用从Float Steinberg抖动algorithm的stackoverflow得到的一段代码。

它是如下所示。但它没有正确抖动图像预期。有没有人有这个正确的执行或任何人可以纠正下面的代码。 实际要求是将24 bit彩色图像转换为1 bit灰度抖动图像。

我认为下面的floyd steinberg方法部分是正确的,但在此之前,方法被称为一些function被称为我不知道。我是非常新的opencv.it是一个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); } 

在这里输入图像说明

抖动错误时使用的偏移量。 例如,您正在更改i-1处的像素,这是您已处理的上一行。 基本上,你有X和Y交换。

将代码更改为:

 for(int k=0;k<3;k++) { int quant_error = (int)img.at<cv::Vec3b>(i,j)[k] - newpixel[k]; 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] + (7 * quant_error) / 16)); if(i+1 < img.rows && j-1 >= 0) 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(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] + (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)); } 

只有3/4的图像抖动的原因是因为传入的图像有4个通道,你正在处理它,就好像它有3个。你可以通过使用img.at<cv::Vec4b>而不是img.at<cv::Vec3b>

如果你想修改抖动,你可以使用一个不同的错误扩散内核 。 弗洛伊德·斯坦伯格使用7 3 5 1模式,但你可以使用不同的模式和内核大小抖动不同的数量和不同的特点。 例如,你可以弥散小于错误的总量。 弗洛伊德·斯坦伯格散布所有的错误,因为7/16 + 3/16 + 5/16 + 1/16 = 1,但你可以select加起来不到一个的条款。 阿特金森抖动 (苹果Macintoshes使用的types)仅扩散了错误的6/8,这使得它具有更高的对比度。 不同的内核将有不同的斑点模式和“外观”。 如果你只想有一个“抖动量”的控件,只需要一个介于0和1之间的值,然后用你的内核的每个项乘以它。 在代码中实现这一点的一个简单方法是将7 3 5 1乘以介于0和256之间的一个代表抖动量的值,然后除以4096而不是16。