Pages: 1 2
Source code in action
Below is some code which simply rotates an image a certain number of radians. Bilinear interpolation is used in order to produce a quality final result. OpenMP is used so that high performance can be achieved on multi-core systems even though this code was made more for demonstration purposes than performance purposes.
/* This code is brought to you by the supercomputingblog.com You may use this code for any purpose. However, you may redistribute this code only if this comment remains intact. */ unsigned int getR(unsigned int in) { return ((in & 0x00ff0000) >> 16); } unsigned int getG(unsigned int in) { return ((in & 0x0000ff00) >> 8); } unsigned int getB(unsigned int in) { return ((in & 0x000000ff)); } void CImageProcessor::Rotate(Bitmap * pBitmap, double angle) { // This function effectively rotates an image, with bilinear filtering int width = pBitmap->GetWidth(); int height = pBitmap->GetHeight(); double cX = (double)width/2.0f; double cY = (double)height/2.0f; // This code uses GDI+. For performance reasons, the bitmap is locked so we can work with the memory directly BitmapData bitmapData; pBitmap->LockBits(&Rect(0,0,pBitmap->GetWidth(), pBitmap->GetHeight()), ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData); unsigned int *pRawBitmapOrig = (unsigned int*)bitmapData.Scan0; // for easy access and indexing unsigned int *pBitmapCopy = new unsigned int[bitmapData.Stride*height/4]; memcpy(pBitmapCopy, pRawBitmapOrig, (bitmapData.Stride*height/4) * sizeof(unsigned int)); int nPixels = height*bitmapData.Stride/4; #pragma omp parallel for for (int i=0; i < height; i++) { double relY = cY-i; // get center y coordinate for (int j=0; j < width; j++) { double relX = j-cX; // get center x coordinate // do rotation transformation double xPrime = relX*cos(angle) + relY*sin(angle); double yPrime = -1 * relX*sin(angle) + relY*cos(angle); // re-center pixel xPrime += cX; yPrime += cY; // There are four nearest original pixels, q11, q12, q21, and q22 // While we may get away with using only four variables, this code // seperates out the x and y of each point for clarity reasons. // Most compilers should be capable of optimizing away the redundant // steps here. int q12x = (int)floor(xPrime); int q12y = (int)floor(yPrime); q12x = max(0, q12x); q12y = max(0, q12y); q12x = min(width-1, q12x); q12y = min(height-1, q12y); int q22x = (int)ceil(xPrime); int q22y = q12y; q22x = min(width-1, q22x); q22x = max(0, q22x); int q11x = q12x; int q11y = (int)ceil(yPrime); q11y = min(height-1, q11y); q11y = max(0, q11y); int q21x = q22x; int q21y = q11y; // We need to get the four nearest neighbooring pixels. // Pixels which are past the border of the image are clamped to the border already. unsigned int q11 = pBitmapCopy[q11y*bitmapData.Stride/4 + q11x]; unsigned int q12 = pBitmapCopy[q12y*bitmapData.Stride/4 + q12x]; unsigned int q21 = pBitmapCopy[q21y*bitmapData.Stride/4 + q21x]; unsigned int q22 = pBitmapCopy[q22y*bitmapData.Stride/4 + q22x]; // Here, it would be better to use a vector class to store the R,G and B values // to keep code consise. But they have been seperated out for maximum clarity // and simplicity. unsigned int q11r = getR(q11); unsigned int q11g = getG(q11); unsigned int q11b = getB(q11); unsigned int q12r = getR(q12); unsigned int q12g = getG(q12); unsigned int q12b = getB(q12); unsigned int q21r = getR(q21); unsigned int q21g = getG(q21); unsigned int q21b = getB(q21); unsigned int q22r = getR(q22); unsigned int q22g = getG(q22); unsigned int q22b = getB(q22); double factor1; double factor2; if ( q21x == q11x ) // special case to avoid divide by zero { factor1 = 1; // They're at the same X coordinate, so just force the calculatione to one point factor2 = 0; } else { factor1 = (((double)q21x - (double)xPrime)/((double)q21x - (double)q11x)); factor2 = (((double)xPrime - (double)q11x)/((double)q21x - (double)q11x)); } double R1r = factor1 * (double)q11r + factor2*(double)q21r; double R1g = factor1 * (double)q11g + factor2*(double)q21g; double R1b = factor1 * (double)q11b + factor2*(double)q21b; double R2r = factor1 * (double)q12r + factor2*(double)q22r; double R2g = factor1 * (double)q12g + factor2*(double)q22g; double R2b = factor1 * (double)q12b + factor2*(double)q22b; double factor3; double factor4; if (q12y == q11y) // special case to avoid divide by zero { factor3 = 1; factor4 = 0; } else { factor3 = ((double) q12y - yPrime)/((double)q12y - (double)q11y); factor4 = (yPrime - (double)q11y)/((double)q12y - (double)q11y); } // Calculate the final unbounded RGB values unsigned int finalR = (unsigned int)((factor3 * R1r) + (factor4*R2r)); unsigned int finalG = (unsigned int)((factor3 * R1g) + (factor4*R2g)); unsigned int finalB = (unsigned int)((factor3 * R1b) + (factor4*R2b)); // Clamp the RBG values to a value between 0 and 255 finalR = min(255, finalR); finalR = max(0, finalR); finalG = min(255, finalG); finalG = max(0, finalG); finalB = min(255, finalB); finalB = max(0, finalB); unsigned int finalPixel = 0xff000000 | (finalR << 16) | (finalG << 8) | finalB; pRawBitmapOrig[(height-i-1)*bitmapData.Stride/4 + j] = finalPixel; //pRawBitmapOrig[(height-i-1)*bitmapData.Stride/4 + j] = q11; // This produces non-interpolated image for comparison // Note that we actually flip the image here to put it back into screen coordinates. } } delete[] pBitmapCopy; pBitmap->UnlockBits(&bitmapData); }
Pages: 1 2