Algorithm to downscale / interpolate an image using buffers? - c++

I'm trying to downscale an image from a large size (as large as 960x960) to possibly as small as 32x32. I have the following code I use to get the raw pixels:
Image* img = new Image();
img->initWithImageFile(fileNameWithPath);
int x=3;
if(img->hasAlpha()){
x=4;
}
unsigned char *data = new unsigned char[img->getDataLen()*x];
data = img->getData();
// [0][0] => Left-Top Pixel !
// But cocos2d Location Y-axis is Bottom(0) to Top(max)
//This is for changing pixels in the original image
//Skip this loop if there are no changes to be made (converting to grayscale etc).
for(int i=0;i<img->getWidth();i++)
{
for(int j=0;j<img->getHeight();j++)
{
unsigned char *pixel = data + (i + j * img->getWidth()) * x;
// You can see/change pixels' RGBA value(0-255) here !
unsigned char r = *pixel;
unsigned char g = *(pixel + 1);
unsigned char b = *(pixel + 2) ;
unsigned char a = *(pixel + 3);
//pixel[2] = 255; //Example: Setting the blue component to 255
}
}
I can create an output image by doing:
int width = scale*img->getWidth();
int height = scale*img->getHeight();
Image* scaledImage = new Image();
auto dataLen = width * height * x * sizeof(unsigned char);
auto data2 = static_cast<unsigned char*>(malloc(dataLen));
scaledImage->initWithRawData(data2, dataLen, width, height, 8);
And I can set the individual pixels of the output image by doing:
unsigned char *pixel2 = data2 + (i + j * width) * x;
The question is how to average / interpolate the pixels from the original image efficiently (using minimum cpu and memory, with the preference to use more cpu if necessary and less memory).
Challenges:
The downscaled image and the original image may not be perfect
multiples.
The downscaled image can be as small as 0.1 of the
original image size.
most images will be downscaled to 0.4 to 0.1 of the original image, so these are the most important ranges.
EDIT: If you think some other interpolation algorithm would be better suited (instead of bilinear) then I am open to it. The challenge is writing an efficient algorithm to average / interpolate the individual pixels..
How do I interpolate the individual pixels of the original image ?

Memory isn't an issue, you need an input buffer with the original image and an output buffer with the rescaled image, you don't need any more.
Bilinear interpolation isn't very suitable for downsampling by a large factor as it doesn't really differ from nearest sampling. You only interpolate four neighbouring pixels and so are vulnerable to aliasing effects, particularly on computer-generated images.
This function uses the averaging method.
/*
resize an image using the averaging method.
Note that dwidth and dheight must be smaller than or equal to swidth, sheight.
*/
void sprshrink(unsigned char *dest, int dwidth, int dheight, unsigned char *src, int swidth, int sheight)
{
int x, y;
int i, ii;
float red, green, blue, alpha;
float xfrag, yfrag, xfrag2, yfrag2;
float xt, yt, dx, dy;
int xi, yi;
dx = ((float)swidth)/dwidth;
dy = ((float)sheight)/dheight;
for(yt= 0, y=0;y<dheight;y++, yt += dy)
{
yfrag = ceil(yt) - yt;
if(yfrag == 0)
yfrag = 1;
yfrag2 = yt+dy - (float) floor(yt + dy);
if(yfrag2 == 0 && dy != 1.0f)
yfrag2 = 1;
for(xt = 0, x=0;x<dwidth;x++, xt+= dx)
{
xi = (int) xt;
yi = (int) yt;
xfrag = (float) ceil(xt) - xt;
if(xfrag == 0)
xfrag = 1;
xfrag2 = xt+dx - (float) floor(xt+dx);
if(xfrag2 == 0 && dx != 1.0f)
xfrag2 = 1;
red = xfrag * yfrag * src[(yi*swidth+xi)*4];
green = xfrag * yfrag * src[(yi*swidth+xi)*4+1];
blue = xfrag * yfrag * src[(yi*swidth+xi)*4+2];
alpha = xfrag * yfrag * src[(yi*swidth+xi)*4+3];
for(i=0; xi + i + 1 < xt+dx-1; i++)
{
red += yfrag * src[(yi*swidth+xi+i+1)*4];
green += yfrag * src[(yi*swidth+xi+i+1)*4+1];
blue += yfrag * src[(yi*swidth+xi+i+1)*4+2];
alpha += yfrag * src[(yi*swidth+xi+i+1)*4+3];
}
red += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4];
green += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+1];
blue += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+2];
alpha += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+3];
for(i=0; yi+i+1 < yt +dy-1 && yi + i+1 < sheight;i++)
{
red += xfrag * src[((yi+i+1)*swidth+xi)*4];
green += xfrag * src[((yi+i+1)*swidth+xi)*4+1];
blue += xfrag * src[((yi+i+1)*swidth+xi)*4+2];
alpha += xfrag * src[((yi+i+1)*swidth+xi)*4+3];
for (ii = 0; xi + ii + 1 < xt + dx - 1 && xi + ii + 1 < swidth; ii++)
{
red += src[((yi+i+1)*swidth+xi+ii+1)*4];
green += src[((yi+i+1)*swidth+xi+ii+1)*4+1];
blue += src[((yi+i+1)*swidth+xi+ii+1)*4+2];
alpha += src[((yi+i+1)*swidth+xi+ii+1)*4+3];
}
if (yi + i + 1 < sheight && xi + ii + 1 < swidth)
{
red += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4];
green += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+1];
blue += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+2];
alpha += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+3];
}
}
if (yi + i + 1 < sheight)
{
red += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4];
green += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 1];
blue += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 2];
alpha += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 3];
for (ii = 0; xi + ii + 1 < xt + dx - 1 && xi + ii + 1 < swidth; ii++)
{
red += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4];
green += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 1];
blue += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 2];
alpha += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 3];
}
}
if (yi + i + 1 < sheight && xi + ii + 1 < swidth)
{
red += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4];
green += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 1];
blue += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 2];
alpha += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 3];
}
red /= dx * dy;
green /= dx * dy;
blue /= dx * dy;
alpha /= dx * dy;
red = clamp(red, 0, 255);
green = clamp(green, 0, 255);
blue = clamp(blue, 0, 255);
alpha = clamp(alpha, 0, 255);
dest[(y*dwidth+x)*4] = (unsigned char) red;
dest[(y*dwidth+x)*4+1] = (unsigned char) green;
dest[(y*dwidth+x)*4+2] = (unsigned char) blue;
dest[(y*dwidth+x)*4+3] = (unsigned char) alpha;
}
}
}
It is maintained in the Baby X resource compiler on github.
https://github.com/MalcolmMcLean/babyxrc

Related

Converting RGB to Luv

i'm trying to convert an rgb image to Luv, i have some problem. The L component is good, but when i show the u and v component both are black(all pixels have value 0).
for (int i = 0; i<height; i++)
for (int j = 0; j<width; j++)
{
Vec3b v3 = src.at<Vec3b>(i, j);
float b = ((float)v3[0]) / 255;
float g = ((float)v3[1]) / 255;
float r = ((float)v3[2]) / 255;
float x = r * 0.412453 + g * 0.357580 + b * 0.180423;
float y = r * 0.212671 + g * 0.715160 + b * 0.072169;
float z = r * 0.019334 + g * 0.119193 + b * 0.950227;
//L
if (y > 0.008856) {
l_mat.at<uchar>(i, j) = 255 / 100 * (116 * pow(y, 1.0 / 3.0));
dst.at<Vec3b>(i, j)[0] = 255 / 100 * (116 * pow(y, 1.0 / 3.0));
// printf("%d / " , l_mat.at<uchar>(i, j));
}
else {
l_mat.at<uchar>(i, j) = 255 / 100 * (903.3 * y);
dst.at<Vec3b>(i, j)[0] = 255 / 100 * (903.3 * y);
}
float u = 4 * x / (x + 15 * y + 3 * z);
float v = 9 * y / (x + 15 * y + 3 * z);
//printf("u: %.2f , v:%.2f || ", u, v);
//U
u_mat.at<uchar>(i, j) = 255 / 354 * (13 * l_mat.at<uchar>(i, j)*(u - 0.19793943) + 134);
//printf("%d / ", u_mat.at<uchar>(i, j));
dst.at<Vec3b>(i, j) = 255 / 354 * (13 * l_mat.at<uchar>(i, j)*(u - 0.19793943) + 134);
//v
v_mat.at<uchar>(i, j) = 255 / 262 * (13 * l_mat.at<uchar>(i, j)*(v - 0.46831096)+140);
dst.at<Vec3b>(i, j) = 255 / 262 * (13 * l_mat.at<uchar>(i, j)*(v - 0.46831096) + 140);
}
I have to do the conversions pixel by pixel, i can't use cvtcolor.

Why does this downscaling algorithm produce artifacts?

I have a scaling algorithm which appears to scale the image to the right size, but produces artefacts(slight image corruption) in the right half of the image. As I am inexperienced using pointers, I suspect I may have blundered with my pointer arithmetic!
To run the project on OSX:
1. Download from :https://www.dropbox.com/s/myme1z1mkxjwyjf/artifact.zip?dl=0
Open the xcodeproj file found in proj.ios
All code that is of relevance, is in HelloWorldScene.cpp
In function test(), you can comment out / uncomment the method we wish to test.:
void HelloWorld::test(){
testCopy(); //In this case the image appears as expected. (a simple copy)
// testScale(); //In this case there are strange artifacts on the right tip of the arrow.
}
Test copy, is my attempt at just copying the contents of the buffer without doing anything bad like memory corruption, leaking etc... The image appears on the screen looking ok!
void HelloWorld::testCopy(){
std::string infile = _imageName;
Image* img = new Image();
img->initWithImageFile(infile);
auto odata = img->getData();
Image* copy = new Image();
int components = 4;
auto finalDataLen = img->getDataLen();
auto finalData = static_cast<unsigned char*>(malloc(finalDataLen));
for (int i = 0; i<img->getWidth(); i++) {
for (int j = 0; j<img->getHeight(); j++) {
unsigned char *pixel = odata + (i + j * img->getWidth()) * components;
unsigned char *fpixel = finalData + (i + j * img->getWidth()) * components;
fpixel[0] = pixel[0];
fpixel[1] = pixel[1];
fpixel[2] = pixel[2];
fpixel[3] = pixel[3];
}
}
copy->initWithRawData(finalData, finalDataLen, img->getWidth(), img->getHeight(), 8);
Texture2D* tk = new Texture2D();
tk->initWithImage(copy);
Sprite* foo = Sprite::createWithTexture(tk);
foo->setPosition(Director::getInstance()->getVisibleSize().width/2,Director::getInstance()->getVisibleSize().height/2);
foo->setScale(0.8);
this->addChild(foo);
delete img;
delete copy;
return;
}
Now comment out testCopy(); and uncomment testScale(); In this case the image appears but with some corruption the right side of the image!
void HelloWorld::testScale(){
std::string infile = _imageName;
Image* img = new Image();
img->initWithImageFile(infile);
Image* scl = new Image();
scaleImage(img, scl, 0.8);
Texture2D* tk = new Texture2D(); //Texture is needed as long as the sprite exists, so we aren't deleting it.
tk->initWithImage(scl);
Sprite* foo = Sprite::createWithTexture(tk);
foo->setPosition(Director::getInstance()->getVisibleSize().width/2,Director::getInstance()->getVisibleSize().height/2);
this->addChild(foo);
delete img;
delete scl;
return;
}
void HelloWorld::scaleImage(Image* original,Image* scaledImage,const float& scale){
int width = scale*original->getWidth();
int height = scale*original->getHeight();
int x=4;
unsigned char* data = original->getData();
auto dataLen = width * height * x * sizeof(unsigned char);
auto data2 = static_cast<unsigned char*>(malloc(dataLen));
//sprshrink seems to be the problem method.
sprshrink(data2, width, height, data, original->getWidth(), original->getHeight());
scaledImage->initWithRawData(data2, dataLen, width, height, 8);
}
//Why does this method produce artifcats ?
void HelloWorld::sprshrink(unsigned char *dest, int dwidth, int dheight, unsigned char *src, int swidth, int sheight){
int x, y;
int i, ii;
float red, green, blue, alpha;
float xfrag, yfrag, xfrag2, yfrag2;
float xt, yt, dx, dy;
int xi, yi;
dx = ((float)swidth)/dwidth;
dy = ((float)sheight)/dheight;
for(yt= 0, y=0;y<dheight;y++, yt += dy)
{
yfrag = (float) ceil(yt) - yt;
if(yfrag == 0)
yfrag = 1;
yfrag2 = yt+dy - (float) floor(yt + dy);
if(yfrag2 == 0 && dy != 1.0f)
yfrag2 = 1;
for(xt = 0, x=0;x<dwidth;x++, xt+= dx)
{
xi = (int) xt;
yi = (int) yt;
xfrag = (float) ceil(xt) - xt;
if(xfrag == 0)
xfrag = 1;
xfrag2 = xt+dx - (float) floor(xt+dx);
if(xfrag2 == 0 && dx != 1.0f)
xfrag2 = 1;
red = xfrag * yfrag * src[(yi*swidth+xi)*4];
green = xfrag * yfrag * src[(yi*swidth+xi)*4+1];
blue = xfrag * yfrag * src[(yi*swidth+xi)*4+2];
alpha = xfrag * yfrag * src[(yi*swidth+xi)*4+3];
for(i=0; xi + i + 1 < xt+dx-1; i++)
{
red += yfrag * src[(yi*swidth+xi+i+1)*4];
green += yfrag * src[(yi*swidth+xi+i+1)*4+1];
blue += yfrag * src[(yi*swidth+xi+i+1)*4+2];
alpha += yfrag * src[(yi*swidth+xi+i+1)*4+3];
}
red += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4];
green += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+1];
blue += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+2];
alpha += xfrag2 * yfrag * src[(yi*swidth+xi+i+1)*4+3];
for(i=0; yi+i+1 < yt +dy-1 && yi + i+1 < sheight;i++)
{
red += xfrag * src[((yi+i+1)*swidth+xi)*4];
green += xfrag * src[((yi+i+1)*swidth+xi)*4+1];
blue += xfrag * src[((yi+i+1)*swidth+xi)*4+2];
alpha += xfrag * src[((yi+i+1)*swidth+xi)*4+3];
for (ii = 0; xi + ii + 1 < xt + dx - 1 && xi + ii + 1 < swidth; ii++)
{
red += src[((yi+i+1)*swidth+xi+ii+1)*4];
green += src[((yi+i+1)*swidth+xi+ii+1)*4+1];
blue += src[((yi+i+1)*swidth+xi+ii+1)*4+2];
alpha += src[((yi+i+1)*swidth+xi+ii+1)*4+3];
}
red += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4];
green += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+1];
blue += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+2];
alpha += xfrag2 * src[((yi+i+1)*swidth+xi+ii+1)*4+3];
}
if (yi + i + 1 < sheight)
{
red += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4];
green += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 1];
blue += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 2];
alpha += xfrag * yfrag2 * src[((yi + i + 1)*swidth + xi) * 4 + 3];
for (ii = 0; xi + ii + 1 < xt + dx - 1 && xi + ii + 1 < swidth; ii++)
{
red += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4];
green += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 1];
blue += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 2];
alpha += yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 3];
}
}
if (yi + i + 1 < sheight && x + xi + 1 < swidth)
{
red += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4];
green += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 1];
blue += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 2];
alpha += xfrag2 * yfrag2 * src[((yi + i + 1)*swidth + xi + ii + 1) * 4 + 3];
}
red /= dx * dy;
green /= dx * dy;
blue /= dx * dy;
alpha /= dx * dy;
red = clamp(red, 0, 255);
green = clamp(green, 0, 255);
blue = clamp(blue, 0, 255);
alpha = clamp(alpha, 0, 255);
dest[(y*dwidth+x)*4] = (unsigned char) red;
dest[(y*dwidth+x)*4+1] = (unsigned char) green;
dest[(y*dwidth+x)*4+2] = (unsigned char) blue;
dest[(y*dwidth+x)*4+3] = (unsigned char) alpha;
}
}
}
I suspect my downscaling algorithm (sprshrink) works (because it is someone elses! :D), and suspect that I am blundering with my usage of pointers in testScale()! What do you think ? Am I allocating and using my pointers properly? What am I doing wrong?
Images:
Clear:
Artefacts when running testScale() instead of testCopy() (comment out testCopy).

1555 DXT1 decompression giving incorrect image output

I am trying, in C++, to decompress 1555 DXT1 textures into RGBA 8888, storing the output into a std::string.
I have successfully decompressed 565 DXT1 to RGBA 8888 using the squish lib, but just can't seem to get 1555 working.
The program isn't crashing, and the output image looks almost correct, but there are several pixels in random places that are strange colours, as you can see in the output image below.
Here's the code.
using namespace std;
string CTexture::extractRGBAData(void)
{
string strPixels;
strPixels.resize(m_usImageSize[0] * m_usImageSize[1] * 4);
for (unsigned long i = 0, j = m_usImageSize[0] * m_usImageSize[1] * 4; i < j; i++)
{
strPixels[i] = 0;
}
if (m_strImageData.length() == 0)
{
return strPixels;
}
unsigned long uiDXTCompressionType;
if (m_uiPlatformId == 8) // GTA III, VC
{
uiDXTCompressionType = m_ucDXTCompressionType;
}
else if (m_uiPlatformId == 9) // SA
{
//uiDXTCompressionType = m_uiAlpha;
uiDXTCompressionType = m_ucDXTCompressionType;
}
else if (m_uiPlatformId == 5) // XBOX, Android
{
uiDXTCompressionType = m_uiAlpha;
}
if (uiDXTCompressionType == DXT1)
{
unsigned long uiWidth = m_usImageSize[0];
unsigned long uiHeight = m_usImageSize[1];
if (m_uiRasterFormat == FORMAT_1555)
{
unsigned long
uiPixelKey = 0,
uiTexelSeek = 0;
for (unsigned long y = 0; y < uiHeight; y += 4)
{
for (unsigned long x = 0; x < uiWidth; x += 4)
{
string strTexel = m_strImageData.substr(uiTexelSeek, 8);
unsigned char *pPixels = new unsigned char[16 * 4];
unsigned char *pBlock = new unsigned char[8];
memcpy(pBlock, strTexel.c_str(), 8);
decompress_DXT1_1555(pPixels, pBlock);
for (unsigned long yOffset = 0; yOffset < 4; yOffset++)
{
for (unsigned long xOffset = 0; xOffset < 4; xOffset++)
{
unsigned long uiPixelKey = (y * uiWidth) + x + (yOffset * uiWidth) + xOffset;
//CDebugger::log("uiPixelKey: " + CStringUtility::toString(uiPixelKey) + ", x: " + CStringUtility::toString(x) + ", y: " + CStringUtility::toString(y) + ", xOffset: " + CStringUtility::toString(xOffset) + ", yOffset: " + CStringUtility::toString(yOffset));
uiPixelKey *= 4;
if (uiPixelKey < strPixels.size()) // this checks if the height has a remainder when dividing by 4 (as the iteration does 4x4 block of pixels)
{
strPixels[uiPixelKey + 0] = pPixels[(((yOffset * 4) + xOffset) * 4) + 2] & 0xFF;
strPixels[uiPixelKey + 1] = pPixels[(((yOffset * 4) + xOffset) * 4) + 1] & 0xFF;
strPixels[uiPixelKey + 2] = pPixels[(((yOffset * 4) + xOffset) * 4) + 0] & 0xFF;
strPixels[uiPixelKey + 3] = 255;// pPixels[(((yOffset * 4) + xOffset) * 4) + 3] & 0xFF;
}
}
}
delete[] pPixels;
delete[] pBlock;
uiTexelSeek += 8;
}
}
}
}
}
void CTexture::decompress_DXT1_1555(unsigned char *pixels, unsigned char *block)
{
string strArea = string((char*)block, 8);
string strPaletteStr = strArea.substr(0, 4);
unsigned long uiIndexes = CStringUtility::unpackULong(strArea.substr(4, 4), false);
unsigned char ucPalette[4][4];
double fPalette[4][4];
unsigned short usPaletteInt[2];
usPaletteInt[0] = CStringUtility::unpackUShort(strPaletteStr.substr(0, 2), false); // 1555
usPaletteInt[1] = CStringUtility::unpackUShort(strPaletteStr.substr(2, 2), false); // 1555
// based on: http://www.glassechidna.com.au/2009/devblogs/s3tc-dxt1dxt5-texture-decompression/
float red, green, blue, alpha;
alpha = (usPaletteInt[0] >> 15) & 1;
red = ((float)((usPaletteInt[0] >> 10) & 0x1F) * 255.0 + 16.0);
red = ((red / 32.0) + red) / 32.0;
green = ((float)((usPaletteInt[0] >> 5) & 0x1F) * 255.0 + 16.0);
green = ((green / 32.0) + green) / 32.0;
blue = ((float)(usPaletteInt[0] & 0x1F)) * 255.0 + 16.0;
blue = ((blue / 32.0) + blue) / 32.0;
fPalette[0][0] = red;
fPalette[0][1] = green;
fPalette[0][2] = blue;
fPalette[0][3] = alpha;
alpha = (usPaletteInt[1] >> 15) & 1;
red = ((float)((usPaletteInt[1] >> 10) & 0x1F) * 255.0 + 16.0);
red = ((red / 32.0) + red) / 32.0;
green = ((float)((usPaletteInt[1] >> 5) & 0x1F) * 255.0 + 16.0);
green = ((green / 32.0) + green) / 32.0;
blue = ((float)(usPaletteInt[1] & 0x1F)) * 255.0 + 16.0;
blue = ((blue / 32.0) + blue) / 32.0;
fPalette[1][0] = red;
fPalette[1][1] = green;
fPalette[1][2] = blue;
fPalette[1][3] = alpha;
// fetch other 2 colours in palette, interpolated between min/max colours
if (usPaletteInt[0] > usPaletteInt[1])
{
fPalette[2][0] = (2.0 * fPalette[0][0] + fPalette[1][0]) / 3.0;
fPalette[2][1] = (2.0 * fPalette[0][1] + fPalette[1][1]) / 3.0;
fPalette[2][2] = (2.0 * fPalette[0][2] + fPalette[1][2]) / 3.0;
fPalette[2][3] = 255;
fPalette[3][0] = (fPalette[0][0] + 2.0 * fPalette[1][0]) / 3.0;
fPalette[3][1] = (fPalette[0][1] + 2.0 * fPalette[1][1]) / 3.0;
fPalette[3][2] = (fPalette[0][2] + 2.0 * fPalette[1][2]) / 3.0;
fPalette[3][3] = 255;
}
else
{
fPalette[2][0] = (fPalette[0][0] + fPalette[1][0]) / 2.0;
fPalette[2][1] = (fPalette[0][1] + fPalette[1][1]) / 2.0;
fPalette[2][2] = (fPalette[0][2] + fPalette[1][2]) / 2.0;
fPalette[2][3] = 255;
fPalette[3][0] = 0;
fPalette[3][1] = 0;
fPalette[3][2] = 0;
fPalette[3][3] = 255; // transparent black
}
for (unsigned long i5 = 0; i5 < 4; i5++)
{
ucPalette[i5][0] = fPalette[i5][0];
ucPalette[i5][1] = fPalette[i5][1];
ucPalette[i5][2] = fPalette[i5][2];
ucPalette[i5][3] = fPalette[i5][3];
}
for (unsigned long i2 = 0; i2<16; i2++)
{
unsigned char index = (uiIndexes >> (i2 * 2)) & 3;
unsigned char colour[4];
colour[0] = ((unsigned char)ucPalette[index][0]) & 0xFF;
colour[1] = ((unsigned char)ucPalette[index][1]) & 0xFF;
colour[2] = ((unsigned char)ucPalette[index][2]) & 0xFF;
colour[3] = ((unsigned char)ucPalette[index][3]) & 0xFF;
// store colour
pixels[(i2 * 4) + 0] = colour[0] & 0xFF;
pixels[(i2 * 4) + 1] = colour[1] & 0xFF;
pixels[(i2 * 4) + 2] = colour[2] & 0xFF;
pixels[(i2 * 4) + 3] = colour[3] & 0xFF;
}
}
I think you're misunderstanding how DXT1 works a bit.
There isn't any alpha in the 2 base colors. They're both in 5:6:5.
The "alpha" is only coming from the case where c0 <= c1. If the block fits this condition, then any pixel with the index 3 will be fully transparent (the 1 bit of alpha is inferred from that).
So... read 5:6:5 (and set alpha=255 for those) instead of 1:5:5:5 in the base colors, and change your alpha on the "transparent black" case from 0,0,0,255 to 0,0,0,0 (actually transparent black instead of opaque black), and you should get better results.

Visualize un-distorted images received from the Leap motion cameras using OpenCV

I want to use OpenCV to visualize undistorted images, obtained after correction of raw images taken from Leap Motion cameras;
according to the documentation,
https://developer.leapmotion.com/documentation/cpp/devguide/Leap_Images.html
the following code should return corrected images: am I right?
unsigned char destination[320][120];
//define needed variables outside the inner loop
float calibrationX, calibrationY;
float weightX, weightY;
float dX, dX1, dX2, dX3, dX4;
float dY, dY1, dY2, dY3, dY4;
int x1, x2, y1, y2;
int denormalizedX, denormalizedY;
int i, j;
const unsigned char* raw = image.data();
const float* distortion_buffer = image.distortion();
//Local variables for values needed in loop
const int distortionWidth = image.distortionWidth();
const int width = image.width();
const int height = image.height();
for (i = 0; i < destinationWidth; i++) {
for (j = 0; j < destinationHeight; j++) {
//Calculate the position in the calibration map (still with a fractional part)
calibrationX = 63 * i/destinationWidth;
calibrationY = 62 * (1 - j/destinationHeight); // The y origin is at the bottom
//Save the fractional part to use as the weight for interpolation
weightX = calibrationX - truncf(calibrationX);
weightY = calibrationY - truncf(calibrationY);
//Get the x,y coordinates of the closest calibration map points to the target pixel
x1 = calibrationX; //Note truncation to int
y1 = calibrationY;
x2 = x1 + 1;
y2 = y1 + 1;
//Look up the x and y values for the 4 calibration map points around the target
dX1 = distortion_buffer[x1 * 2 + y1 * distortionWidth];
dX2 = distortion_buffer[x2 * 2 + y1 * distortionWidth];
dX3 = distortion_buffer[x1 * 2 + y2 * distortionWidth];
dX4 = distortion_buffer[x2 * 2 + y2 * distortionWidth];
dY1 = distortion_buffer[x1 * 2 + y1 * distortionWidth + 1];
dY2 = distortion_buffer[x2 * 2 + y1 * distortionWidth + 1];
dY3 = distortion_buffer[x1 * 2 + y2 * distortionWidth + 1];
dY4 = distortion_buffer[x2 * 2 + y2 * distortionWidth + 1];
//Bilinear interpolation of the looked-up values:
// X value
dX = dX1 * (1 - weightX) * (1 - weightY) +
dX2 * weightX * (1 - weightY) +
dX3 * (1 - weightX) * weightY +
dX4 * weightX * weightY;
// Y value
dY = dY1 * (1 - weightX) * (1 - weightY) +
dY2 * weightX * (1 - weightY) +
dY3 * (1 - weightX) * weightY +
dY4 * weightX * weightY;
// Reject points outside the range [0..1]
if((dX >= 0) && (dX <= 1) && (dY >= 0) && (dY <= 1)) {
//Denormalize from [0..1] to [0..width] or [0..height]
denormalizedX = dX * width;
denormalizedY = dY * height;
//look up the brightness value for the target pixel
destination[i][j] = raw[denormalizedX + denormalizedY * width];
} else {
destination[i][j] = -1;
}
}
}
Now, I'm using OpenCV to visualize undistorted image:
Mat imgCorrected(120,320,CV_8UC1);
for(int i = 0; i < 120; i++)
for(int j = 0; j < 320; j++)
imgCorrected.at<unsigned char>(i,j) = destination[i][j];
imshow("ImgCorrected", imgCorrected);
And this is the result:
Result
I really don't know what I'm doing wrong.
Thanks for any help.

Gradient algorithm produces little white dots

I'm working on an algorithm to generate point to point linear gradients. I have a rough proof of concept implementation done:
GLuint OGLENGINEFUNCTIONS::CreateGradient( std::vector<ARGBCOLORF> &input,POINTFLOAT start, POINTFLOAT end, int width, int height,bool radial )
{
std::vector<POINT> pol;
std::vector<GLubyte> pdata(width * height * 4);
std::vector<POINTFLOAT> linearpts;
std::vector<float> lookup;
float distance = GetDistance(start,end);
RoundNumber(distance);
POINTFLOAT temp;
float incr = 1 / (distance + 1);
for(int l = 0; l < 100; l ++)
{
POINTFLOAT outA;
POINTFLOAT OutB;
float dirlen;
float perplen;
POINTFLOAT dir;
POINTFLOAT ndir;
POINTFLOAT perp;
POINTFLOAT nperp;
POINTFLOAT perpoffset;
POINTFLOAT diroffset;
dir.x = end.x - start.x;
dir.y = end.y - start.y;
dirlen = sqrt((dir.x * dir.x) + (dir.y * dir.y));
ndir.x = static_cast<float>(dir.x * 1.0 / dirlen);
ndir.y = static_cast<float>(dir.y * 1.0 / dirlen);
perp.x = dir.y;
perp.y = -dir.x;
perplen = sqrt((perp.x * perp.x) + (perp.y * perp.y));
nperp.x = static_cast<float>(perp.x * 1.0 / perplen);
nperp.y = static_cast<float>(perp.y * 1.0 / perplen);
perpoffset.x = static_cast<float>(nperp.x * l * 0.5);
perpoffset.y = static_cast<float>(nperp.y * l * 0.5);
diroffset.x = static_cast<float>(ndir.x * 0 * 0.5);
diroffset.y = static_cast<float>(ndir.y * 0 * 0.5);
outA.x = end.x + perpoffset.x + diroffset.x;
outA.y = end.y + perpoffset.y + diroffset.y;
OutB.x = start.x + perpoffset.x - diroffset.x;
OutB.y = start.y + perpoffset.y - diroffset.y;
for (float i = 0; i < 1; i += incr)
{
temp = GetLinearBezier(i,outA,OutB);
RoundNumber(temp.x);
RoundNumber(temp.y);
linearpts.push_back(temp);
lookup.push_back(i);
}
for (unsigned int j = 0; j < linearpts.size(); j++) {
if(linearpts[j].x < width && linearpts[j].x >= 0 &&
linearpts[j].y < height && linearpts[j].y >=0)
{
pdata[linearpts[j].x * 4 * width + linearpts[j].y * 4 + 0] = (GLubyte) j;
pdata[linearpts[j].x * 4 * width + linearpts[j].y * 4 + 1] = (GLubyte) j;
pdata[linearpts[j].x * 4 * width + linearpts[j].y * 4 + 2] = (GLubyte) j;
pdata[linearpts[j].x * 4 * width + linearpts[j].y * 4 + 3] = (GLubyte) 255;
}
}
lookup.clear();
linearpts.clear();
}
return CreateTexture(pdata,width,height);
}
It works as I would expect most of the time, but at certain angles it produces little white dots. I can't figure out what does this.
This is what it looks like at most angles (good) http://img9.imageshack.us/img9/5922/goodgradient.png
But once in a while it looks like this (bad): http://img155.imageshack.us/img155/760/badgradient.png
What could be causing the white dots?
Is there maybe also a better way to generate my gradients if no solution is possible for this?
Thanks
I think you have a bug indexing into the pdata byte vector. Your x domain is [0, width) but when you multiply out the indices you're doing x * 4 * width. It should probably be x * 4 + y * 4 * width or x * 4 * height + y * 4 depending on whether you're data is arranged row or column major.