PDA

View Full Version : Posterizes an image with results identical to Gimp's Posterize command



suseway
28th November 2010, 11:13
How can i posterizes an image with results identical to Gimp's Posterize command? i've written the following code which make posterize with value 5, but it doesn't work correctly, cause the result image is not an identical to gimp's posterize tool at 5 value. it differs a little when i increase scale to 400% and look at result image and the image after gimp's posterize... any idea?



for(int x=0; x<image.width(); x++)
for(int y=0; y<image.height(); y++)
result.setPixel(x, y, 51+51*(int)(qGray(image.pixel(x, y))/51));



PS: Posterize - it's reduce number of colors (is available, for ex., in GIMP at "Colors -> Posterize...")

squidge
28th November 2010, 11:42
Are you using exact same algorithm as GIMP ?

suseway
28th November 2010, 11:56
I don't know, which algorythm use gimp, but i want make 100% effect as gimp does

javimoya
28th November 2010, 14:53
gimp is open source...
http://www.gimp.org/source/#source

SixDegrees
28th November 2010, 16:23
There are dozens of ways to reduce colors in an image - simple decimation; color map reindexing; median cut algorithm and many others, many of which also operate over a variety of color spaces.

Unless you know precisely which algorithm the Gimp uses, along with how it is implemented, the only way to make your results "100% effect as gimp does" is to use the Gimp through scripting and a system call. And even that may not be identical across versions of the Gimp. Or you could examine their source code - keeping in mind that reuse of someone else's code opens the door to a complex of legal issues - but even this won't guarantee that your results will be the same across past or future versions of the Gimp.

Why do you care that the results match exactly?

suseway
28th November 2010, 18:52
I had looked at gimp sources and found in gimpoperationposterize.c:



static gboolean
gimp_operation_posterize_process (GeglOperation *operation,
void *in_buf,
void *out_buf,
glong samples,
const GeglRectangle *roi)
{
GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
GimpPosterizeConfig *config = GIMP_POSTERIZE_CONFIG (point->config);
gfloat *src = in_buf;
gfloat *dest = out_buf;
gfloat levels;

if (! config)
return FALSE;

levels = config->levels - 1.0;

while (samples--)
{
dest[RED_PIX] = RINT (src[RED_PIX] * levels) / levels;
dest[GREEN_PIX] = RINT (src[GREEN_PIX] * levels) / levels;
dest[BLUE_PIX] = RINT (src[BLUE_PIX] * levels) / levels;
dest[ALPHA_PIX] = src[ALPHA_PIX];

src += 4;
dest += 4;
}

return TRUE;
}



Seems that the code do this posterize operation... but i don't know yet what exactly this code does and how can i rewrite this for Qt..

SixDegrees
28th November 2010, 19:58
The code is dirt simple. Each pixel is stored as a quadruple value of red, green, blue and alpha, each represented as a float ranging from 0 to 1. The alpha channel is simply copied. The other channels are multiplied by the number of levels desired, converted to an integer by rounding, multiplied by the number of levels and stored in the corresponding output pixel channel. It's plain-vanilla C code, and should be trivial to convert to Qt image operations.

Again, however, there is no guarantee that this algorithm won't change in subsequent Gimp releases, or that is matches what has been implemented in the past.

suseway
1st December 2010, 14:11
Hello. I'm trying to implement this algorythm to Qt image operations, but it's create new original image (instead of posterized image).
Here is my code:


int r, g, b, a;
QRgb val;
double levels = 5.0; // posterize value 5.0
levels -= 1.0; // by following gimp code
int newRed, newGreen, newBlue;
// starting loop
for(int x=0; y<img.width(); x++)
for(int y=0; y<img.height(); y++) {
val = img.pixel(x, y);
r = qRed(val);
g = qGreen(val);
b = qBlue(val);
a = qAlpha(val);

qDebug() << "r = " << r;
qDebug() << "g = " << g;
qDebug() << "b = " << b;
// lines 21-23 from GIMP...
newRed = (rint) ( (double)r / 255.0 * levels) / levels;
newGreen = (rint) ( (double)g / 255.0 * levels) / levels;
newBlue = (rint) ( (double)b / 255.0 * levels) / levels;

qDebug() << "newRed = " << newRed;
qDebug() << "newGreen = " << newGreen;
qDebug() << "newBlue = " << newBlue;

// make picture by direct pixel operation
postherized_img.setPixel(x, y, qRgba(newRed, newGreen, newBlue, a));
}
// save the picture
postherized_img.save("/home/user/Desktop/test_posterized.jpg", "JPG", 100);


What i do wrong?

avila
15th May 2015, 22:37
Hello. I'm trying to implement this algorythm to Qt image operations, but it's create new original image (instead of posterized image).
Here is my code:


int r, g, b, a;
QRgb val;
double levels = 5.0; // posterize value 5.0
levels -= 1.0; // by following gimp code
int newRed, newGreen, newBlue;
// starting loop
for(int x=0; y<img.width(); x++)
for(int y=0; y<img.height(); y++) {
val = img.pixel(x, y);
r = qRed(val);
g = qGreen(val);
b = qBlue(val);
a = qAlpha(val);

qDebug() << "r = " << r;
qDebug() << "g = " << g;
qDebug() << "b = " << b;
// lines 21-23 from GIMP...
newRed = (rint) ( (double)r / 255.0 * levels) / levels;
newGreen = (rint) ( (double)g / 255.0 * levels) / levels;
newBlue = (rint) ( (double)b / 255.0 * levels) / levels;

qDebug() << "newRed = " << newRed;
qDebug() << "newGreen = " << newGreen;
qDebug() << "newBlue = " << newBlue;

// make picture by direct pixel operation
postherized_img.setPixel(x, y, qRgba(newRed, newGreen, newBlue, a));
}
// save the picture
postherized_img.save("/home/user/Desktop/test_posterized.jpg", "JPG", 100);


What i do wrong?


You have to round the values correctly. Look my code (it worked and generated an identical result as GIMP).



QImage img = inputImg.toImage();
QRgb val;
int levels = 2.0; // Posterize level two;
levels--;
double sr, sg, sb;
int dr, dg, db;
for (int i=0; i<img.width(); ++i) {
for (int j=0; j<img.height(); ++j) {
val = img.pixel(i, j);
sr = qRed(val)/255.0;
sg = qGreen(val)/255.0;
sb = qBlue(val)/255.0; // to make sr, sg, sb between 0 and 1
dr = 255 * qRound(sr * levels)/levels;
dg = 255 * qRound(sg * levels)/levels;
db = 255 * qRound(sb * levels)/levels; // qRound does the same as RINT (rounding and NOT TRUNCATING its argument).
img.setPixel(i, j, qRgb(dr, dg, db));
}
}
outputImg = QPixmap::fromImage(img);


Please notice that I didn't care about the alpha channel because it's a JPG image (no alpha) and it was not modified. But if the source image has an alpha channel you MUST copy the information to the destination (and change the function qRgb to qRgba, and the type QRgb to QRgba).

EDIT: Oh my god! I've answered a thread from 5 (FIVE!!) years ago [=P]

d_stranz
17th May 2015, 16:46
Oh my god! I've answered a thread from 5 (FIVE!!) years ago

Well, at least the OP can finally get on with this project...