Array to image alternatives - c++

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;
}

Related

Issue with convolution kernels that add up to zero

I'm making an image editing program in c++ using sfml and tried to add image filters using:
int clamp(int value, int min, int max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
void MyImage::applyKernel(std::vector<std::vector<int>> kernel)
{
int index(0), tempx(0), tempy(0);
int wr(0), wg(0), wb(0), wa(0), sum(0);
auto newPixels = new sf::Uint8[this->size_y * this->size_x * 4];
// Calculate the sum of the kernel
for (int i = 0; i < kernel.size(); i++) {
for (int j = 0; j < kernel[i].size(); j++) {
sum += kernel[i][j];
}
}
for (int y = 0; y < this->size_y; y++) {
for (int x = 0; x < this->size_x; x++) {
/*
Calculate weighted sum from kernel
*/
wr = wg = wb = wa = 0;
for (int i = 0; i < kernel.size(); i++) {
for (int j = 0; j < kernel[i].size(); j++) {
/*
Calculates the coordinates of the kernel relative to the pixel we are changing
*/
tempx = x + (j - floor(kernel[i].size() / 2));
tempy = y + (i - floor(kernel.size() / 2));
//std::cout << "kernel=(" << j << ", " << i << "), pixel=(" << x << ", " << y << ") tempPos=(" << tempx << ", " << tempy << ")\n";
/*
This code below should have the effect of mirroring the image in the case the kernel coordinate is out of bounds (along the edge of the image)
*/
tempx = (tempx < 0) ? -1 * tempx : tempx;
tempy = (tempy < 0) ? -1 * tempy : tempy;
tempx = (tempx > this->size_x) ? x - (j - floor(kernel[i].size() / 2)) : tempx;
tempy = (tempy > this->size_y) ? y - (i - floor(kernel.size() / 2)) : tempy;
if (tempx >= 0 && tempx < this->size_x && tempy >= 0 && tempy < this->size_y) {
index = (((tempy * this->size_x) - tempy) + (tempx)) * 4;
wr += kernel[i][j] * this->pixels[index];
wg += kernel[i][j] * this->pixels[index + 1];
wb += kernel[i][j] * this->pixels[index + 2];
wa += kernel[i][j] * this->pixels[index + 3];
}
}
}
if (sum) {
wr /= sum;
wg /= sum;
wb /= sum;
wa /= sum;
}
index = (((y * this->size_x) - y) + (x)) * 4;
newPixels[index] = clamp(wr, 0, 255); // Red
newPixels[index + 1] = clamp(wg, 0, 255); // Green
newPixels[index + 2] = clamp(wb, 0, 255); // Blue
newPixels[index + 3] = clamp(wa, 0, 255); // Alpha
}
}
this->pixels = newPixels;
// Copies the data from our sf::Uint8 array to the image object to be displayed => Removes the overhead of calling setPixel(x,y,color) for every pixel {As a side note setPixel() should always be avoided}|
this->im->create(this->size_x, this->size_y, this->pixels);
}
I was trying to use [-1,-1,-1], [-1,8,-1]. [-1,-1,-1] for edge detection but just ended up with a white image except for some pixels near the bottom. I've tried different images and kernels out but any that add to 0 don't work. For example if I take the edge detection kernel above and change the 8 to a 9, it gives an expected result. Is there something wrong with my idea of how convolution kernels work or is it just a bug in my code?
Thank you.

Labelling on an 2d array C++ got the wrong result

I am newbie and having work with connected components labelling algorithm.
My purpose is that I need to find out 3 block of light points and then calculate the coordinates of the central point of each block (kind of image processing).
But after I run the for loop, I got the same coordinate for all the central points of three blocks, and don't know what was going wrong.
Could someone here please help me!
Thanks a lot!
This is my code
for (size_t i = 0; i < 128; i++)
{
for (size_t j = 0; j < 128; j++)
{
if (pInt[i * 128 + j] <= 18000) label[i][j] = 0;
if (pInt[i * 128 + j] > 18000)
{
if (label[i-1][j-1] != 0)
{
label[i][j] = label[i-1][j-1];
}
if (label[i-1][j] != 0)
{
label[i][j] = label[i-1][j];
}
if (label[i-1][j+1] != 0)
{
label[i][j] = label[i-1][j+1];
}
if (label[i][j-1] != 0)
{
label[i][j] = label[i][j-1];
}
if ((label[i - 1][j - 1] = 0) && (label[i - 1][j] = 0) && (label[i - 1][j + 1] = 0) && (label[i][j - 1] = 0))
{
l = l + 1;
label[i][j] = l;
}
}
if (label[i][j] = 1)
{
count1++;
sumx1 = sumx1 + i;
sumy1 = sumy1 + j;
}
if (label[i][j] = 2)
{
count2++;
sumx2 = sumx2 + i;
sumy2 = sumy2 + j;
}
if (label[i][j] = 3)
{
count3++;
sumx3 = sumx3 + i;
sumy3 = sumy3 + j;
}
}
}
float y1 = (float)sumx1 / count1;
float z1 = (float)sumy1 / count1;
float y2 = (float)sumx2 / count2;
float z2 = (float)sumy2 / count2;
float ya = (float)sumx3 / count3;
float za = (float)sumy3 / count3;
printf("three points:\n1(%f, %f)\n2(%f, %f)\na(%f, %f)\n", z1 - 64, 64 - y1, z2 - 64, 64 - y2, za - 64, 64 - ya);
In your if statements you need to use the == operator to compare. The single = is assignment. For example:
if (label[i][j] == 1)
There are 6 places I see where you need to make this change.

SDL2.0 screen nullptr on render of Window

Hey so I'm relatively new to the SDL library and just trying to get to grips with it.
I found a C++ conversion for Minecraft4k but it was based on SDL1.x so I'm trying to convert it to SDL2.0
At present the build is successful, but when it gets to;
plot(x, y, rgbmul(col, fxmul(br, ddist)));
It throws a read access violation exception:
screen was nullptr
This is my code;
// C++ port of Minecraft 4k JS (http://jsdo.it/notch/dB1E)
// By The8BitPimp
// See: the8bitpimp.wordpress.com
#include <SDL.h>
#include <math.h>
#include <windows.h>
#include <tchar.h>
#include "plot.h"
#include "llist.h"
const int w = 320;
const int h = 240;
SDL_Surface *screen = nullptr;
const float math_pi = 3.14159265359f;
static inline float math_sin(float x) {
return sinf(x);
}
static inline float math_cos(float x) {
return cosf(x);
}
// the texture map
int texmap[16 * 16 * 16 * 3];
// the voxel map
char map[64 * 64 * 64];
static inline int random(int max) {
return (rand() ^ (rand() << 16)) % max;
}
static inline void plot(int x, int y, int c) {
int *p = (int*)screen->pixels;
p[y * w + x] = c;
}
static void makeTextures(void) {
// each texture
for (int j = 0; j<16; j++) {
int k = 255 - random(96);
// each pixel in the texture
for (int m = 0; m<16 * 3; m++)
for (int n = 0; n<16; n++) {
int i1 = 0x966C4A;
int i2 = 0;
int i3 = 0;
if (j == 4)
i1 = 0x7F7F7F;
if ((j != 4) || (random(3) == 0))
k = 255 - random(96);
if (j == 1)
{
if (m < (((n * n * 3 + n * 81) >> 2) & 0x3) + 18)
i1 = 0x6AAA40;
else if (m < (((n * n * 3 + n * 81) >> 2) & 0x3) + 19)
k = k * 2 / 3;
}
if (j == 7)
{
i1 = 0x675231;
if ((n > 0) && (n < 15) && (((m > 0) && (m < 15)) || ((m > 32) && (m < 47))))
{
i1 = 0xBC9862;
i2 = n - 7;
i3 = (m & 0xF) - 7;
if (i2 < 0)
i2 = 1 - i2;
if (i3 < 0)
i3 = 1 - i3;
if (i3 > i2)
i2 = i3;
k = 196 - random(32) + i2 % 3 * 32;
}
else if (random(2) == 0)
k = k * (150 - (n & 0x1) * 100) / 100;
}
if (j == 5)
{
i1 = 0xB53A15;
if (((n + m / 4 * 4) % 8 == 0) || (m % 4 == 0))
i1 = 0xBCAFA5;
}
i2 = k;
if (m >= 32)
i2 /= 2;
if (j == 8)
{
i1 = 5298487;
if (random(2) == 0)
{
i1 = 0;
i2 = 255;
}
}
// fixed point colour multiply between i1 and i2
i3 =
((((i1 >> 16) & 0xFF) * i2 / 255) << 16) |
((((i1 >> 8) & 0xFF) * i2 / 255) << 8) |
((i1 & 0xFF) * i2 / 255);
// pack the colour away
texmap[n + m * 16 + j * 256 * 3] = i3;
}
}
}
static void makeMap(void) {
// add random blocks to the map
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
for (int z = 0; z < 64; z++) {
int i = (z << 12) | (y << 6) | x;
float yd = (y - 32.5) * 0.4;
float zd = (z - 32.5) * 0.4;
map[i] = random(16);
float th = random(256) / 256.0f;
if (th > sqrtf(sqrtf(yd * yd + zd * zd)) - 0.8f)
map[i] = 0;
}
}
}
}
static void init(void) {
makeTextures();
makeMap();
}
// fixed point byte byte multiply
static inline int fxmul(int a, int b) {
return (a*b) >> 8;
}
// fixed point 8bit packed colour multiply
static inline int rgbmul(int a, int b) {
int _r = (((a >> 16) & 0xff) * b) >> 8;
int _g = (((a >> 8) & 0xff) * b) >> 8;
int _b = (((a)& 0xff) * b) >> 8;
return (_r << 16) | (_g << 8) | _b;
}
static void render(void) {
float now = (float)(SDL_GetTicks() % 10000) / 10000.f;
float xRot = math_sin(now * math_pi * 2) * 0.4 + math_pi / 2;
float yRot = math_cos(now * math_pi * 2) * 0.4;
float yCos = math_cos(yRot);
float ySin = math_sin(yRot);
float xCos = math_cos(xRot);
float xSin = math_sin(xRot);
float ox = 32.5 + now * 64.0;
float oy = 32.5;
float oz = 32.5;
// for each column
for (int x = 0; x < w; x++) {
// get the x axis delta
float ___xd = ((float)x - (float)w / 2.f) / (float)h;
// for each row
for (int y = 0; y < h; y++) {
// get the y axis delta
float __yd = ((float)y - (float)h / 2.f) / (float)h;
float __zd = 1;
float ___zd = __zd * yCos + __yd * ySin;
float _yd = __yd * yCos - __zd * ySin;
float _xd = ___xd * xCos + ___zd * xSin;
float _zd = ___zd * xCos - ___xd * xSin;
int col = 0;
int br = 255;
float ddist = 0;
float closest = 32.f;
// for each principle axis x,y,z
for (int d = 0; d < 3; d++) {
float dimLength = _xd;
if (d == 1)
dimLength = _yd;
if (d == 2)
dimLength = _zd;
float ll = 1.0f / (dimLength < 0.f ? -dimLength : dimLength);
float xd = (_xd)* ll;
float yd = (_yd)* ll;
float zd = (_zd)* ll;
float initial = ox - floor(ox);
if (d == 1) initial = oy - floor(oy);
if (d == 2) initial = oz - floor(oz);
if (dimLength > 0) initial = 1 - initial;
float dist = ll * initial;
float xp = ox + xd * initial;
float yp = oy + yd * initial;
float zp = oz + zd * initial;
if (dimLength < 0) {
if (d == 0) xp--;
if (d == 1) yp--;
if (d == 2) zp--;
}
// while we are concidering a ray that is still closer then the best so far
while (dist < closest) {
// quantize to the map grid
int tex = map[(((int)zp & 63) << 12) | (((int)yp & 63) << 6) | ((int)xp & 63)];
// if this voxel has a texture applied
if (tex > 0) {
// find the uv coordinates of the intersection point
int u = ((int)((xp + zp) * 16.f)) & 15;
int v = ((int)(yp * 16.f) & 15) + 16;
// fix uvs for alternate directions?
if (d == 1) {
u = ((int)(xp * 16.f)) & 15;
v = (((int)(zp * 16.f)) & 15);
if (yd < 0)
v += 32;
}
// find the colour at the intersection point
int cc = texmap[u + v * 16 + tex * 256 * 3];
// if the colour is not transparent
if (cc > 0) {
col = cc;
ddist = 255 - ((dist / 32 * 255));
br = 255 * (255 - ((d + 2) % 3) * 50) / 255;
// we now have the closest hit point (also terminates this ray)
closest = dist;
}
}
// advance the ray
xp += xd;
yp += yd;
zp += zd;
dist += ll;
}
}
plot(x, y, rgbmul(col, fxmul(br, ddist)));
}
}
}
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *screen;
screen = SDL_CreateWindow(
"Minecraft4k", // window title
SDL_WINDOWPOS_CENTERED, // initial x position
SDL_WINDOWPOS_CENTERED, // initial y position
320, // width, in pixels
240, // height, in pixels
SDL_WINDOW_OPENGL // flags - see below
);
SDL_Renderer* renderer;
renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
if (screen == nullptr) {
return 1;
}
init();
bool running = true;
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
running &= (event.type != SDL_QUIT);
}
SDL_RenderPresent(renderer);
render();
}
SDL_DestroyWindow(screen);
SDL_Quit();
return 0;
}
When I actually run the code I do get a black screen, but the debugger lands on the line
plot(x, y, rgbmul(col, fxmul(br, ddist)));
in ;
static void render(void)
This is all just "for fun" so any information or guidance is appreciated.
You define screen twice (the first time as a global variable, the second time within your main), but you initialize it only once (within your main).
Because of that, the global variable screen actually is set to nullptr and plot fails trying to use it, as the error message states.

Sobel Edge Detection without a buffer

For an embedded design I am attempting to implement sobel's edge detection on a board without the use of a buffer. i.e. I am reading and writing directly from the screen. I can however, store about one or two imge width full of data to be referenced later. This is due to limitations set forth by the board. However I have fallen into some issue. All that I recieve is noise regardless if I attempt to do sobel or another edge detection algorithm. The code is below, does anyone have any suggestions
Version 1
void sobelEdgeDetection2() {
int GX[3][3];
int GY[3][3];
int sumX[3];
int sumY[3];
int SUM[3];
int piX = 0;
int piY = 0;
//uint8_t R, G, B = 0;
int I, J = 0;
//UnpackedColour pixVal;
uint16_t *buffer;
// allocate space for even scan lines and odd scan lines
buffer = new uint16_t[_gl->getWidth()];
//buffer for previous line
uint16_t *bufT;
// allocate space for even scan lines and odd scan lines
bufT = new uint16_t[_gl->getWidth()];
// Masks //////////////////////////////////////
//X//
GX[0][0] = -1;
GX[0][1] = 0;
GX[0][2] = 1;
GX[1][0] = -2;
GX[1][1] = 0;
GX[1][2] = 2;
GX[2][0] = -1;
GX[2][1] = 0;
GX[2][2] = 1;
//Y//
GY[0][0] = 1;
GY[0][1] = 2;
GY[0][2] = 1;
GY[1][0] = 0;
GY[1][1] = 0;
GY[1][2] = 0;
GY[2][0] = -1;
GY[2][1] = -2;
GY[2][2] = -1;
for (int Y = 0; Y < _gl->getHeight(); Y++) {
for (int X = 0; X < _gl->getWidth(); X++) {
sumX[0] = sumX[1] = sumX[2] = 0;
sumY[0] = sumY[1] = sumY[2] = 0;
if (Y == 0 || Y == _gl->getHeight() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else if (X == 0 || X == _gl->getWidth() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else {
for (I = -1; I <= 1; I++) {
for (J = -1; J <= 1; J++) {
piX = J + X;
piY = I + Y;
pixel16 pix = getPixel(piX, piY);
uint8_t Red = pix.Red;
uint8_t Green = pix.Green;
uint8_t Blue = pix.Blue;
sumX[0] += (Red) * GX[J + 1][I + 1];
sumX[1] += (Green) * GX[J + 1][I + 1];
sumX[2] += (Blue) * GX[J + 1][I + 1];
sumY[0] += (Red) * GY[J + 1][I + 1];
sumY[1] += (Green) * GY[J + 1][I + 1];
sumY[2] += (Blue) * GY[J + 1][I + 1];
}
}
SUM[0] = abs(sumX[0]) + abs(sumY[0]);
SUM[1] = abs(sumX[1]) + abs(sumY[1]);
SUM[2] = abs(sumX[2]) + abs(sumY[2]);
}
if (SUM[0] > 255)
SUM[0] = 255;
if (SUM[0] < 0)
SUM[0] = 0;
if (SUM[1] > 255)
SUM[1] = 255;
if (SUM[1] < 0)
SUM[1] = 0;
if (SUM[2] > 255)
SUM[2] = 255;
if (SUM[2] < 0)
SUM[2] = 0;
int newPixel[3];
newPixel[0] = (255 - ((unsigned char) (SUM[0])));
newPixel[1] = (255 - ((unsigned char) (SUM[1])));
newPixel[2] = (255 - ((unsigned char) (SUM[2])));
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
buffer[X] = packColour(pix).packed565;
}
//Need to move cursor back
// draw it
this->paintRow(Point(0, Y), buffer, _gl->getWidth());
}
delete[] buffer;
}
Version2
/**
* https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/image-processing/edge_detection.html
* 1 Iterate over every pixel in the image
* 2 Apply the x gradient kernel
* 3 Apply the y gradient kernel
* 4 Find the length of the gradient using pythagoras' theorem
* 5 Normalise the gradient length to the range 0-255
* 6 Set the pixels to the new values
*/
void sobelEdgeDetection4() {
UnpackedColour colour;
for (int x = 1; x < _gl->getWidth() - 1; x++) {
for (int y = 1; y < _gl->getHeight() - 1; y++) {
// initialise Gx and Gy to 0
int Gx = 0;
int Gy = 0;
unsigned int intensity = 0;
// Left column
pixel16 pixel = this->getPixel(x - 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += -intensity;
pixel = this->getPixel(x - 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -2 * intensity;
pixel = this->getPixel(x - 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += +intensity;
// middle column
pixel = this->getPixel(x, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += -2 * intensity;
pixel = this->getPixel(x, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += +2 * intensity;
// right column
pixel = this->getPixel(x + 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += -intensity;
pixel = this->getPixel(x + 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +2 * intensity;
pixel = this->getPixel(x + 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += +intensity;
// calculate the gradient length
unsigned int length = (unsigned int) sqrt(
(float) (Gx * Gx) + (float) (Gy * Gy));
// normalise the length to 0 to 255
length = length / 17;
// draw the pixel on the edge image
pixel16 pixel2(length,length,length);
this->setPixel(x, y, pixel2);
}
}
}
Version 3
// sobel map for the x axis
const double _SOBEL_Gx[3][3] = { { -1.0, +0.0, +1.0 }, { -2.0, +0.0, +2.0 },
{ -1.0, +0.0, +1.0 } };
// sobel map for the y axis
const double _SOBEL_Gy[3][3] = { { +1.0, +2.0, +1.0 }, { +0.0, +0.0, +0.0 },
{ -1.0, -2.0, -1.0 } };
double get_sobel_gradient(int width, int height, int x, int y) {
double sobel_gradient_x = 0, sobel_gradient_y = 0;
int mx = 0, my = 0, sx = 0, sy = 0;
for (mx = x; mx < x + 3; mx++) {
sy = 0;
for (my = y; my < y + 3; my++) {
if (mx < width && my < height) {
//int r, g, b, idx;
int idx = (mx + width * my) * 3;
pixel16 pixVal = this->getPixel(idx);
//r = pixVal.Red;
//g = pixVal.Green;
//b = pixVal.Blue;
UnpackedColour col = this->packColour(pixVal);
sobel_gradient_x += col.packed565 * _SOBEL_Gx[sx][sy];
sobel_gradient_y += col.packed565 * _SOBEL_Gy[sx][sy];
}
sy++;
}
sx++;
}
return abs(sobel_gradient_x) + abs(sobel_gradient_y);
}
void sobelEdgeDetection3() {
double threshold = 50000.0;
UnpackedColour colour;
for (int y = 0; y < _gl->getHeight(); y++) {
for (int x = 0; x < _gl->getWidth(); x++) {
if (get_sobel_gradient(_gl->getWidth(), _gl->getHeight(), x, y)
>= threshold) {
colour.packed565 = 0x0000; //set white
} else {
colour.packed565 = 0xFFFF; //set black
}
this->setPixel(x, y, colour);
}
}
}
For Version 1, after you allocate 2 buffers (just use buffer and bufT), create 2 pointers to point to the current and previous rows, like this:
uint16_t *currentRow = buffer;
uint16_t *prevRow = bufT;
Inside the row loop, write to currentRow instead of buffer:
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
currentRow[X] = packColour(pix).packed565;
Because the Sobel filter reads from the previous row, you can't overwrite a row until after you have finished calculating the filtered values for the row after it. So at the end of the loop, where you are currently calling paintRow(), draw the previous row (if one exists), and then swap the buffers so that the current becomes the previous, and the previous becomes the new current row (to be overwritten on the next pass through the loop). On the last row the current row is also drawn, because otherwise it won't be since the outer loop is about to terminate.
if(Y > 0) // draw the previous row if this is not the first row:
this->paintRow(Point(0, Y-1), prevRow, _gl->getWidth());
if(Y == _gl->getHeight()-1) // draw the current row if it is the last:
this->paintRow(Point(0, Y), currentRow, _gl->getWidth());
// swap row pointers:
uint16_t *temp = prevRow;
prevRow = currentRow;
currentRow = temp;
The same strategy should work for the other versions.

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.