Converting yuv 4:2:0 file to Rgb not getting expected output - c++

The logic I am using is that I am reading he yuv file in a buffer and using 3 pointers to point to the Y,U,V components. The image dimension is 1920*1080.
I am
taking 4 y pixels for corresponding 1U,1V pixels.
Extracting the pixel value in integers.
Converting the Y,U,V to R,G,B and storing the components in RGB buffer.
But the video output is incorrect. The output is somewhat like black and white
#include "stdafx.h"
#include <string>
#include <stdio.h>
unsigned char* g_pcRGBbuffer;
unsigned char* g_pcYUVBuffer;
int _tmain(int argc, _TCHAR* argv[])
{
int l_nSize = 1920 * 1080 * 1.5;
g_pcYUVBuffer = new unsigned char[l_nSize];
g_pcRGBbuffer = new unsigned char[1920 * 1080 * 3];
FILE* fp_source;
FILE* fp_rgb = NULL;
int l_nY, l_nU, l_nV;
double l_dR, l_dG, l_dB, l_ni;
fp_source = fopen("D:\\Sample_1920x1080.yuv", "rb");
// converting yuv file to rgb file
if (fp_source)
{
fp_rgb = fopen("D:\\Sample_1920x1080.rgb", "wb+");
while (!feof(fp_source))
{
fread(g_pcYUVBuffer, 1, l_nSize, fp_source);
unsigned char* l_pcY = g_pcYUVBuffer;
unsigned char* l_pcU = l_pcY + 1920 * 1080;
unsigned char* l_pcV = l_pcU + ((1920 * 1080) / 4);
unsigned char* l_pcRGBbuffer = g_pcRGBbuffer;
for (l_ni = 0; l_ni < (1920 * 1080) / 4; l_ni++)
{
l_nY = l_pcY[0];
l_nU = l_pcU[0];
l_nV = l_pcV[0];
l_dR = l_nY + 1.402 * (l_nV - 128);
l_dG = l_nY - 0.34414 * (l_nU - 128) - 0.71414 * (l_nV - 128);
l_dB = l_nY + 1.772 * (l_nU - 128);
// This prevents colour distortions in rgb image
if (l_dR < 0)
l_dR = 0;
else if (l_dR > 255)
l_dR = 255;
if (l_dG < 0)
l_dG = 0;
else if (l_dG > 255)
l_dG = 255;
if (l_dB < 0)
l_dB = 0;
else if (l_dB > 255)
l_dB = 255;
// 1st pixel of RGB
l_pcRGBbuffer[0] = l_dR;
l_pcRGBbuffer[1] = l_dG;
l_pcRGBbuffer[2] = l_dB;
l_nY = l_pcY[1];
l_nU = l_pcU[0];
l_nV = l_pcV[0];
l_dR = l_nY + 1.402 * (l_nV - 128);
l_dG = l_nY - 0.34414 * (l_nU - 128) - 0.71414 * (l_nV - 128);
l_dB = l_nY + 1.772 * (l_nU - 128);
if (l_dR < 0)
l_dR = 0;
else if (l_dR > 255)
l_dR = 255;
if (l_dG < 0)
l_dG = 0;
else if (l_dG > 255)
l_dG = 255;
if (l_dB < 0)
l_dB = 0;
else if (l_dB > 255)
l_dB = 255;
// 2nd pixel of RGB
l_pcRGBbuffer[3] = l_dR;
l_pcRGBbuffer[4] = l_dG;
l_pcRGBbuffer[5] = l_dB;
l_nY = l_pcY[2];
l_nU = l_pcU[0];
l_nV = l_pcV[0];
l_dR = l_nY + 1.402 * (l_nV - 128);
l_dG = l_nY - 0.34414 * (l_nU - 128) - 0.71414 * (l_nV - 128);
l_dB = l_nY + 1.772 * (l_nU - 128);
if (l_dR < 0)
l_dR = 0;
else if (l_dR > 255)
l_dR = 255;
if (l_dG < 0)
l_dG = 0;
else if (l_dG > 255)
l_dG = 255;
if (l_dB < 0)
l_dB = 0;
else if (l_dB > 255)
l_dB = 255;
// 3rd pixel of RGB
l_pcRGBbuffer[6] = l_dR;
l_pcRGBbuffer[7] = l_dG;
l_pcRGBbuffer[8] = l_dB;
l_nY = l_pcY[3];
l_nU = l_pcU[0];
l_nV = l_pcV[0];
// l_dR = 1.164*(l_nY-16 ) + 1.596*(l_nV-128 );
// l_dG = 1.164*(l_nY-16 ) - 0.813*(l_nV-128 ) - 0.391*(l_nU-128);
// l_dB = 1.164*(l_nY-16 ) + 2.018*(l_nU-128 );
l_dR = l_nY + 1.402 * (l_nV - 128);
l_dG = l_nY - 0.34414 * (l_nU - 128) - 0.71414 * (l_nV - 128);
l_dB = l_nY + 1.772 * (l_nU - 128);
if (l_dR < 0)
l_dR = 0;
else if (l_dR > 255)
l_dR = 255;
if (l_dG < 0)
l_dG = 0;
else if (l_dG > 255)
l_dG = 255;
if (l_dB < 0)
l_dB = 0;
else if (l_dB > 255)
l_dB = 255;
// 4th pixel of RGB
l_pcRGBbuffer[9] = l_dR;
l_pcRGBbuffer[10] = l_dG;
l_pcRGBbuffer[11] = l_dB;
l_pcY += 4;
l_pcU += 1;
l_pcV += 1;
l_pcRGBbuffer += 12;
}
fwrite(g_pcRGBbuffer, 1, 1920 * 1080 * 3, fp_rgb);
}
printf("Video converted to rgb file \n ");
}
else
{
printf("fail\n");
}
fclose(fp_rgb);
fclose(fp_source);
return 0;
}

Actually YUV 4:2:0 planar stores all Y pixels first,then U pixels,then V pixels
To extract correct pixels use the following forumla:
//Refer wikipedia for further details
size.total = size.width * size.height;
y = yuv[position.y * size.width + position.x];
u = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total];
v = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total + (size.total / 4)];
YUV2RGBTestApp2.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <string>
#include <stdio.h>
unsigned char *g_pcRGBbuffer;
unsigned char *g_pcYUVBuffer;
int _tmain(int argc, _TCHAR* argv[])
{
int l_nSize = 1920 * 1080 * 1.5;
g_pcYUVBuffer = new unsigned char[l_nSize];
g_pcRGBbuffer = new unsigned char[1920 * 1080 * 3];
FILE *fp_source;
FILE *fp_rgb = NULL;
int l_ny, l_nu, l_nv, l_ni, RGBval;
int l_dr, l_dg, l_db;
fp_source = fopen("D:\\Sample_1920x1080.yuv", "rb");
int l_nj;
//converting yuv file to rgb file
if (fp_source) {
fp_rgb = fopen("D:\\Sample_1920x1080.rgb", "wb");
while (!feof(fp_source))
{
fread(g_pcYUVBuffer, 1, l_nSize, fp_source);
unsigned char *l_pcRGBbuffer = g_pcRGBbuffer;
for (int j = 0; j < 1080; j++)
{
for (int i = 0; i<1920; i++)
{
/*
Position for y,u,v components for yuv planar 4:2:0
Refer wikipedia for further reference
*/
int Y = g_pcYUVBuffer[j * 1920 + i];
int U = g_pcYUVBuffer[((j / 2) * 960) + (i / 2) + (1920 * 1080)];
int V = g_pcYUVBuffer[((j / 2) * 960) + (i / 2) + (1920 * 1080) + ((1920 * 1080) / 4)];
int R = 1.164*(Y - 16) + 1.596*(V - 128);
int G = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
int B = 1.164*(Y - 16) + 2.018*(U - 128);
if (R>255)R = 255;
if (R<0)R = 0;
if (G>255)G = 255;
if (G<0)G = 0;
if (B>255)B = 255;
if (B<0)B = 0;
l_pcRGBbuffer[0] = R;
l_pcRGBbuffer[1] = G;
l_pcRGBbuffer[2] = B;
l_pcRGBbuffer += 3;
}
}
fwrite(g_pcRGBbuffer, 1, 1920 * 1080 * 3, fp_rgb);
}
printf("Video converted to rgb file \n ");
}
else {
printf("fail\n");
}
fclose(fp_rgb);
fclose(fp_source);
return 0;
}

Related

Array to image alternatives

For a project, I'm using the MLX90461 IR camera.
And I'm using some C++ code to read the data of the camera.
The data of this camera is stored in an array of 192 elements (16x12 RES).
In this array, I store the measured temperature for that pixel.
Next, I convert that temperature to RGB values and store those as well.
Because I need 3 values per pixel the array has 576 elements. (192 * 3).
So now I have an array filled with data. And I want to make an image of all this data.
I followed this example and it works great.
Now comes the but... But:
It outputs a .ppm file
The output is just 16x12 pixels
And I want to use the output as a sort of live stream, so the .ppm file type is not really great.
So My questions are.
Is this way of doing things in the right direction?
Is there a way to output a more "common" file type like a .png
Is C++ the best way to do this type of data processing
I was thinking to use Python to make an image based on the array.
The end result I'm liking to have is something like this: visualization on display (see GIF below code)
void print_pixels(){
static uint8_t pixels[576];
uint16_t l = 0;
for(int i = 0 ; i < 12 ; i++){ //rows
for(int j = 0 ; j < 16 ; j++){ // cols
struct CRGB color = tempToColor(temperatures[j+16*i]);
pixels[l] = color.r;
l++;
pixels[l] = color.g;
l++;
pixels[l] = color.b;
l++;
printf("T%4.2f,R%i,G%i,B%i : ", temperatures[j+16*i], pixels[l - 3], pixels[l - 2], pixels[l - 1]);
}
std::cout << "\n\n";
}
FILE *imageFile;
int height=12,width=16;
imageFile=fopen("/home/pi/output_IR_camera.ppm","wb");
if(imageFile==NULL){
perror("ERROR: Cannot open output file");
exit(1);
}
fprintf(imageFile,"P6\n"); // P6 filetype
fprintf(imageFile,"%d %d\n",width,height); // dimensions
fprintf(imageFile,"255\n"); // Max pixel
fwrite(pixels,1,576,imageFile);
fclose(imageFile);
}
struct CRGB tempToColor(float temp) {
struct CRGB color;
if (temp > MAX_TEMP) {
color.r = 255;
color.g = 0;
color.b = 255;
} else if (temp >= MIN_TEMP + PEAK_STEPS * 4) {
color.r = round(255 * (temp - (MIN_TEMP + PEAK_STEPS * 4.0)) / PEAK_STEPS);
color.g = 0;
color.b = 255;
} else if (temp >= MIN_TEMP + PEAK_STEPS * 3) {
color.r = 0;
color.g = round(255 * (6 - (temp - (MIN_TEMP + PEAK_STEPS * 3.0)) / PEAK_STEPS));
color.b = 255;
} else if (temp >= MIN_TEMP + PEAK_STEPS * 2) {
color.r = 0;
color.g = 255;
color.b = round(255 * (temp - (MIN_TEMP + PEAK_STEPS * 2.0)) / PEAK_STEPS);
} else if (temp >= MIN_TEMP + PEAK_STEPS * 1) {
color.r = round(255 * (6 - (temp - (MIN_TEMP + PEAK_STEPS * 1.0)) / PEAK_STEPS));
color.g = 255;
color.b = 0;
} else if (temp >= MIN_TEMP) {
color.r = 255;
color.g = round(255 * ((temp - MIN_TEMP) / PEAK_STEPS));
color.b = 0;
} else {
color.r = 255;
color.g = 0;
color.b = 0;
}
return color;
}

Bitmap blur in c++

iam writing a program to represent a blur efect on a bitmap.
Blur efect code:
for (xx = 0; xx < bitmapInfoHeader.biWidth; xx++)
{
for (yy = 0; yy <bitmapInfoHeader.biHeight; yy++)
{
avgB = avgG = avgR = 0;
Counter = 0;
for (x = xx; x < bitmapInfoHeader.biWidth && x < xx + blurSize; x++)
{
for (y = yy; y < bitmapInfoHeader.biHeight && y < yy + blurSize; y++)
{
avgB += bitmapImage[x *3 + y*bitmapInfoHeader.biWidth * 3 + 0]; //bitmapimage[x][y];
avgG += bitmapImage[x *3 + y*bitmapInfoHeader.biWidth * 3 + 1];
avgR += bitmapImage[x *3 + y*bitmapInfoHeader.biWidth * 3 + 2];
Counter++;
}
}
avgB = avgB / Counter;
avgG = avgG / Counter;
avgR = avgR / Counter;
bitmapImage[xx * 3 + yy*bitmapInfoHeader.biWidth * 3 + 0] = avgB;
bitmapImage[xx * 3 + yy*bitmapInfoHeader.biWidth * 3 + 1] = avgG;
bitmapImage[xx * 3 + yy*bitmapInfoHeader.biWidth * 3 + 2] = avgR;
}
}
And my output function which take the blured Array "bitmapImage" to a new file:
out = fopen(file,"wb");
fwrite(&bitmapInfoHeader, sizeof(char), sizeof(BITMAPINFOHEADER), out);
fwrite(&bitmapFileHeader, sizeof(char), sizeof(BITMAPFILEHEADER), out);
fseek(out, sizeof(char)*bitmapFileHeader.bfOffBits, SEEK_SET);
fwrite(bitmapImage, sizeof(char), bitmapInfoHeader.biSizeImage, out);
The reading function:
BITMAPINFOHEADER bitmapInfoHeader;
FILE *filePtr,*out;
BITMAPFILEHEADER bitmapFileHeader;
unsigned char *bitmapImage = nullptr;
filePtr = fopen(filename, "rb");
if (filePtr == NULL)
return NULL;
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
//bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);
bitmapImage = new unsigned char[bitmapInfoHeader.biSizeImage];
fread(bitmapImage, sizeof(char), bitmapInfoHeader.biSizeImage,filePtr);
*size = bitmapInfoHeader.biSizeImage;
When i compile the program a new bmp file is created but the image viewer cant show it and thorw an error.
So what am doing wrong here? is the blur algorytm good?

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.

RGB to HSI and HSI to RGB conversion

I am trying to covert RGB to HSI and revert it. (The task is required to have it from scratch.)
In RGB to HSI convertion, Saturation and Intensity outputs are fine. But I don't seem to get the problem in the formulation of Hue.
example output:
Red = 255, Green = 255, Blue = 255
Hue = -2147483648, Saturation = 0, Intensity = 255
Red = 252, Green = 255, Blue = 255
Hue = 3, Saturation = 0.00787402, Intensity = 254
I use this calculator to check my outputs.
Please let me know what's wrong. Thanks.
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include "rgb.h"
#include <cmath>
#include <algorithm>
#include <fstream>
using namespace std;
int main()
{
char infname[256];
ofstream outputFile, outputFile2;
outputFile.open("RGB_HSI.txt");
outputFile2.open("HSI_RGB.txt");
cout << "Enter input image : ";
cin >> infname;
IplImage *img = cvLoadImage(infname, 1);
RgbImage pic(img);
int H = img->height;
int W = img->width;
for (int j=0;j<H;j++)
for (int i=0;i<W;i++) {
double temp = 0;
double R =(double) pic[j][i].r;
double G =(double) pic[j][i].g;
double B =(double) pic[j][i].b;
double intensity = 0;
double hue = 0;
double saturation = 0;
int resultHue = 0;
double resultSaturation = 0;
int resultIntensity = 0;
intensity = (R + G + B) / 3;
if ((R + G + B) == 765) {
saturation = 0;
hue = 0;
}
double minimum = min(R, min(G, B));
if (intensity > 0) {
saturation = 1 - minimum / intensity;
}
else if (intensity == 0) {
saturation = 0;
}
temp = (R - (G/2) - (B/2)) / (sqrt((R*R) + (G*G) + (B*B) - (R*G) - (R*B) - (G*B)));
if (G >= B) {
hue = acos(temp);
outputFile<<"1. temp = "<<temp<<", H = "<<hue<<endl;
}
else if (B > G) {
hue = 360 - acos(temp);
outputFile<<"2. temp = "<<temp<<", H = "<<hue<<endl;
}
resultHue = (int) hue;
resultSaturation = saturation;
resultIntensity = (int) intensity;
//outputFile2<<"image = "<<pic[j][i]<<endl;
outputFile<<"Red = "<<R<<", Green = "<<G<<", Blue = "<<B<<endl;
outputFile<<"Hue = "<<resultHue<<", Saturation = "<<resultSaturation<<", Intensity = "<<resultIntensity<<endl;
//converting HSI to RGB
int backR = 0, backG = 0, backB = 0;
if (resultHue == 0){
backR = (int) (resultIntensity + (2 * resultIntensity * resultSaturation));
backG = (int) (resultIntensity - (resultIntensity * resultSaturation));
backB = (int) (resultIntensity - (resultIntensity * resultSaturation));
}
else if ((0 < resultHue) && (resultHue < 120)) {
backR = (int) (resultIntensity + (resultIntensity * resultSaturation) * cos(resultHue) / cos(60-resultHue));
backG = (int) (resultIntensity + (resultIntensity * resultSaturation) * (1 - cos(resultHue) / cos(60-resultHue)));
backB = (int) (resultIntensity - (resultIntensity * resultSaturation));
}
else if ( resultHue == 120 ){
backR = (int) (resultIntensity - (resultIntensity * resultSaturation));
backG = (int) (resultIntensity + (2 * resultIntensity * resultSaturation));
backB = (int) (resultIntensity - (resultIntensity * resultSaturation));
}
else if ((120 < resultHue) && (resultHue < 240)) {
backR = (int) (resultIntensity - (resultIntensity * resultSaturation));
backG = (int) (resultIntensity + (resultIntensity * resultSaturation) * cos(resultHue-120) / cos(180-resultHue));
backB = (int) (resultIntensity + (resultIntensity * resultSaturation) * (1 - cos(resultHue-120) / cos(180-resultHue)));
}
else if (resultHue == 240) {
backR = (int) (resultIntensity - (resultIntensity * resultSaturation));
backG = (int) (resultIntensity - (resultIntensity * resultSaturation));
backB = (int) (resultIntensity + (2 * resultIntensity * resultSaturation));
}
else if ((240 < resultHue) && (resultHue < 360)) {
backR = (int) (resultIntensity + (resultIntensity * resultSaturation) * (1 - cos(resultHue-240) / cos(300-resultHue)));
backG = (int) (resultIntensity - (resultIntensity * resultSaturation));
backB = (int) (resultIntensity + (resultIntensity * resultSaturation) * cos(resultHue-240) / cos(300-resultHue));
}
//outputpic[j][i] = (int) (R + G + B);
//outputFile2<<"output = "<<outputpic[j][i]<<endl;
outputFile2<<"Hue = "<<resultHue<<", Saturation = "<<resultSaturation<<", Intensity = "<<resultIntensity<<endl;
outputFile2<<"Red = "<<backR<<", Green = "<<backG<<", Blue = "<<backB<<endl;
}
outputFile.close();
cout << "\nRGB_HSI values printed as text file: RGB_HSI.text\n";
outputFile2.close();
cout << "\nHSI_RGB values printed as text file: HSI_RGB.text\n";
return 0;
}
The problem is in this line:
temp = (R - (G/2) - (B/2)) / (sqrt((R*R) + (G*G) + (B*B) - (R*G) - (R*B) - (G*B)));
When R = G = B, then you have a division by zero:
R² - G² - B² - RG - RB - GB = R² + R² + R² - R² - R² - R² = 0
I'm actually surprised it didn't crashed...
In that case, just assign 0 to the hue. From your link:
Neutral colors--white, gray, and black--are set to 0° for convenience.
From others answer it looks like there is a divide by zero issue when R = G = B when you calculate temp but also from what I can tell you are using degrees with the trigonometric function but they are expecting radians i.e.:
#include <cmath>
#include <iostream>
int main()
{
double pi = atan(1)*4 ;
std::cout << cos(180) << std::endl ;
std::cout << cos(360) << std::endl ;
std::cout << cos(pi) << std::endl ;
std::cout << cos(2*pi) << std::endl ;
}