OpenCL RGB->HSL and back - opencl

Ive been through every resource and cant fix my problem.
My host code calls the rgb2hsl kernel, then calls the hsl2rgb kernel. I should end up with the same image that I started with, but I do not. My new image hue is off in certain areas.
The red areas should not be there.
Here is the screen shot of what happens:
Here is the original picture
Here is the code:
#define E .0000001f
bool fEqual(float x, float y)
{
return (x+E > y && x-E < y);
}
__kernel void rgb2hsl(__global float *values, int numValues)
{
// thread index and total
int idx = get_global_id(0);
int idxVec3 = idx*3;
float3 gMem;
if (idx < numValues)
{
gMem.x = values[idxVec3];
gMem.y = values[idxVec3+1];
gMem.z = values[idxVec3+2];
}
barrier(CLK_LOCAL_MEM_FENCE);
gMem /= 255.0f; //convert from 256 color to float
//calculate chroma
float M = max(gMem.x, gMem.y);
M = max(M, gMem.z);
float m = min(gMem.x, gMem.y);
m = min(m, gMem.z);
float chroma = M-m; //calculate chroma
float lightness = (M+m)/2.0f;
float saturation = chroma/(1.0f-fabs(2.0f*lightness-1.0f));
float hue = 0;
if (fEqual(gMem.x, M))
hue = (int)((gMem.y - gMem.z)/chroma) % 6;
if (fEqual(gMem.y, M))
hue = (((gMem.z - gMem.x))/chroma) + 2;
if (fEqual(gMem.z, M))
hue = (((gMem.x - gMem.y))/chroma) + 4;
hue *= 60.0f;
barrier(CLK_LOCAL_MEM_FENCE);
if (idx < numValues)
{
values[idxVec3] = hue;
values[idxVec3+1] = saturation;
values[idxVec3+2] = lightness;
}
}
__kernel void hsl2rgb(__global float *values, int numValues)
{
// thread index and total
int idx = get_global_id(0);
int idxVec3 = idx*3;
float3 gMem;
if (idx < numValues)
{
gMem.x = values[idxVec3];
gMem.y = values[idxVec3+1];
gMem.z = values[idxVec3+2];
}
barrier(CLK_LOCAL_MEM_FENCE);
float3 rgb = (float3)(0,0,0);
//calculate chroma
float chroma = (1.0f - fabs( (float)(2.0f*gMem.z - 1.0f) )) * gMem.y;
float H = gMem.x/60.0f;
float x = chroma * (1.0f - fabs( fmod(H, 2.0f) - 1.0f ));
switch((int)H)
{
case 0:
rgb = (float3)(chroma, x, 0);
break;
case 1:
rgb = (float3)(x, chroma, 0);
break;
case 2:
rgb = (float3)(0, chroma, x);
break;
case 3:
rgb = (float3)(0, x, chroma);
break;
case 4:
rgb = (float3)(x, 0, chroma);
break;
case 5:
rgb = (float3)(chroma, 0, x);
break;
default:
rgb = (float3)(0, 0, 0);
}
barrier(CLK_LOCAL_MEM_FENCE);
rgb += gMem.z - .5f*chroma;
rgb *= 255;
if (idx < numValues)
{
values[idxVec3] = rgb.x;
values[idxVec3+1] = rgb.y;
values[idxVec3+2] = rgb.z;
}
}

The problem was this line:
hue = (int)((gMem.y - gMem.z)/chroma) % 6;
It should be
hue = fmod((gMem.y - gMem.z)/chroma, 6.0f);
I did some more changes to remove artifacts:
#define E .0000001f
bool fEqual(float x, float y)
{
return (x+E > y && x-E < y);
}
__kernel void rgb2hsl(__global float *values, int numValues)
{
// thread index and total
int idx = get_global_id(0);
int idxVec3 = idx*3;
float3 gMem;
if (idx < numValues)
{
gMem.x = values[idxVec3];
gMem.y = values[idxVec3+1];
gMem.z = values[idxVec3+2];
}
barrier(CLK_LOCAL_MEM_FENCE);
gMem /= 255.0f; //convert from 256 color to float
//calculate chroma
float M = max(gMem.x, gMem.y);
M = max(M, gMem.z);
float m = min(gMem.x, gMem.y);
m = min(m, gMem.z);
float chroma = M-m; //calculate chroma
float lightness = (M+m)/2.0f;
float saturation = chroma/(1.0f-fabs(2.0f*lightness-1.0f));
float hue = 0;
if (fEqual(gMem.x, M))
hue = fmod((gMem.y - gMem.z)/chroma, 6.0f);
if (fEqual(gMem.y, M))
hue = (((gMem.z - gMem.x))/chroma) + 2;
if (fEqual(gMem.z, M))
hue = (((gMem.x - gMem.y))/chroma) + 4;
hue *= 60.0f;
barrier(CLK_LOCAL_MEM_FENCE);
if (M == m)
hue = saturation = 0;
barrier(CLK_GLOBAL_MEM_FENCE);
if (idx < numValues)
{
//NOTE: ARTIFACTS SHOW UP if we do not cast to integer!
values[idxVec3] = (int)hue;
values[idxVec3+1] = saturation;
values[idxVec3+2] = lightness;
}
}

Related

Libavcodec mpeg4 avi encoding framerate and timebase problem

I try to generate a video with a timebase more precise than the 1/fps (camera frame rate are not constant). But the generated AVI does not seem to take into account the framerate that I indicate.
#include <iostream>
extern "C" {
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
constexpr int TIMEFACTOR = 10;
static void encodeFrame(AVCodecContext *avctx, AVFormatContext *ctx, AVFrame* frame)
{
int ret = avcodec_send_frame(avctx, frame);
AVPacket packet;
av_init_packet(&packet);
ret = 0;
while (ret >= 0) {
ret = avcodec_receive_packet(avctx, &packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return; // nothing to write
};
//packet.pts = (frame) ? frame->pts : packet.pts;
av_packet_rescale_ts(&packet, avctx->time_base, ctx->streams[0]->time_base);
packet.duration = TIMEFACTOR;
av_interleaved_write_frame(ctx, &packet);
av_packet_unref(&packet);
}
}
static void fill_yuv_frame(AVFrame* frame, int width, int height, int frameId)
{
// Y
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + frameId * 3;
}
}
// Cb and Cr
for (int y = 0; y < height / 2; y++) {
for (int x = 0; x < width / 2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + frameId * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + frameId * 5;
}
}
}
int main(int argc, char** argv)
{
const char filename[] = "output.avi";
const char encoder[] = "mpeg4";
constexpr int WIDTH = 640;
constexpr int HEIGHT = 480;
av_log_set_level(AV_LOG_DEBUG);
//delete file because libavcodec reload file
remove(filename);
AVFormatContext* ofmtCtx;
int ret = avformat_alloc_output_context2(&ofmtCtx, NULL, "avi", filename);
AVCodec* avcodec = avcodec_find_encoder_by_name(encoder);
AVCodecContext* avctx = avcodec_alloc_context3(avcodec);
avctx->width = WIDTH ;
avctx->height = HEIGHT ;
avctx->sample_aspect_ratio = { 1, 1 };
avctx->pix_fmt = AV_PIX_FMT_YUV420P; //Do not work with other type
avctx->codec_id = AV_CODEC_ID_MPEG4;
avctx->bit_rate = 4 * 1000 * 1000;
avctx->time_base = av_make_q(1, 25 * TIMEFACTOR);
avctx->framerate = av_make_q(25, 1);
avctx->ticks_per_frame = TIMEFACTOR;
avctx->gop_size = 10;
avctx->max_b_frames = 1;
AVStream* m_outStream = avformat_new_stream(ofmtCtx, NULL);
ret = avcodec_open2(avctx, avcodec, NULL);
ret = avcodec_parameters_from_context(m_outStream->codecpar, avctx);
m_outStream->time_base = avctx->time_base;
m_outStream->r_frame_rate = avctx->framerate;
m_outStream->avg_frame_rate = avctx->framerate;
ret = avio_open(&(ofmtCtx->pb),
filename,
AVIO_FLAG_WRITE);
ret = avformat_write_header(ofmtCtx, NULL);
av_dump_format(ofmtCtx, 0, filename, 1);
AVFrame * avframe = av_frame_alloc();
avframe->format = avctx->pix_fmt;
avframe->width = avctx->width;
avframe->height = avctx->height;
ret = av_frame_get_buffer(avframe, 0);
ret = av_frame_make_writable(avframe);
for (int i = 0; i < 25; ++i) {
fflush(stdout);
fill_yuv_frame(avframe, avctx->width, avctx->height, i);
avframe->pts = i * TIMEFACTOR;
encodeFrame(avctx, ofmtCtx, avframe);
}
encodeFrame(avctx, ofmtCtx, NULL);
av_write_trailer(ofmtCtx);
return 0;
}
And this is dump output :
[mpeg4 # 000002AA64FA1880] intra_quant_bias = 0 inter_quant_bias = -64
[file # 000002AA64F69AC0] Setting default whitelist 'file,crypto'
[avi # 000002AA64F73400] reserve_index_space:0 master_index_max_size:256
[avi # 000002AA64F73400] duration_est:36000.000, filesize_est:18.4GiB, master_index_max_size:256
Output #0, avi, to 'output.avi':
Metadata:
ISFT : Lavf58.12.100
Stream #0:0, 0, 1/250: Video: mpeg4, 1 reference frame (FMP4 / 0x34504D46), yuv420p, 640x480 (0x0) [SAR 1:1 DAR 4:3], 0/1, q=2-31, 4000 kb/s, 25 fps, 25 tbr, 250 tbn
But if I open the video in ffmpeg command line fps is 250, and video content 250 frames, with many dropframe.

CS50 Filter(more) Edges Problem. Why can I not accurately calculate edge pixels?

Hi I am confused as to what is wrong with my code related to the "edges" filter portion of the problem.
I am able to apply a filter that detects edges. For some reason I fail the check50. I am only able to apply the filter to middle pixels. Any guidance would be much appreciated. I am wondering if I am approaching this problem the incorrect way.
With this code I am just ignoring the calculations for the "black pixels" or the pixels outside of the range of height/width.
Here is my code:
void edges(int height, int width, RGBTRIPLE image[height][width])
{
//create temporary array
RGBTRIPLE temp[height][width];
for (int i = 0; i < height; i ++)
{
for (int j = 0; j < width; j++)
{
temp[i][j] = image[i][j];
}
}
//initialize sobel arrays
int gxarray[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
int gyarray[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
//loop through each ith pixel in jth column
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j ++)
{
float gx_red = 0;
float gx_blue = 0;
float gx_green = 0;
float gy_red = 0;
float gy_blue = 0;
float gy_green = 0;
//use the temporary array grid to calculate each gx value
//check if it is a corner or side pixel - and treat that pixel as black pixel
for (int k = -1; k < 2; k ++)
{
for (int l = -1; l < 2; l ++)
{
//calculate the gx and gy for each color by multiply each of
//check if they are corner or sidepixels
if (i + k < 0 || i + k >= height)
{
continue;
}
if (j + l < 0 || j + l >= width)
{
continue;
}
//otherwise calculate each color value
gx_red += temp[i + k][j + l].rgbtRed * gxarray[k + 1][l + 1];
gx_blue += temp[i + k][j + l].rgbtBlue * gxarray[k + 1][l + 1];
gx_green += temp[i + k][j + l].rgbtGreen * gxarray[k + 1][l + 1];
gy_red += temp[i + k][j + l].rgbtRed * gyarray[k + 1][l + 1];
gy_blue += temp[i + k][j + l].rgbtBlue * gyarray[k + 1][l + 1];
gy_green += temp[i + k][j + l].rgbtGreen * gyarray[k + 1][l + 1];
}
}
//times each number by itself then, add them, then square root them
int red = 0 + round(sqrt(gx_red * gx_red + gy_red * gy_red));
int blue = 0 + round(sqrt(gx_blue * gx_blue + gy_blue * gy_blue));
int green = 0 + round(sqrt(gx_green * gx_green + gy_green * gy_green));
image[i][j].rgbtRed = red;
image[i][j].rgbtBlue = blue;
image[i][j].rgbtGreen = green;
//cap it by 255
if (image[i][j].rgbtRed > 255)
{
image[i][j].rgbtRed = 255;
}
if (image[i][j].rgbtBlue > 255)
{
image[i][j].rgbtBlue = 255;
}
if (image[i][j].rgbtGreen > 255)
{
image[i][j].rgbtGreen = 255;
}
}
}
return;
}
```[enter image description here][1]
[1]: https://i.stack.imgur.com/3bExI.png

recursive merge sort repetitive element print

I am facing problem in the below code...
This code is of recursive merge sort but the array which gets printed has repetitive elements from the array.
help me in identifying the problem.
void merge(int arr[], int l, int mid, int h) {
int i = l;
int j = mid + 1;
int k = l;
int b[h + 1];
while (i <= mid && j <= h) {
if (arr[i] < arr[j])
b[k++] = arr[i++];
else
b[k++] = arr[j++];
}
while (i <= mid)
b[k++] = arr[i++];
while (j <= h)
b[k++] = arr[j++];
for (int i = 0; i <= h; i++)
arr[i] = b[i];
}
void Rmerge_sort(int arr[], int l, int h)
{
if (l < h) {
int mid = (h + l) / 2;
Rmerge_sort(arr, l, mid);
Rmerge_sort(arr, mid + 1, h);
merge(arr, l, mid, h);
}
}
int main() {
int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 10 }, n = 10;
Rmerge_sort(arr, 0, n - 1);
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
}
You define array b with size h + 1 instead of h - l + 1. The merge loops copy the elements to the index values l to h, but the final copy loop takes elements from 0 to h, copying elements from an uninitialized part.
Here is a corrected version:
void merge(int arr[], int l, int mid, int h) {
int len = h - l + 1;
int i = l;
int j = mid + 1;
int k = 0;
int b[len];
while (i <= mid && j <= h) {
if (arr[i] < arr[j])
b[k++] = arr[i++];
else
b[k++] = arr[j++];
}
while (i <= mid)
b[k++] = arr[i++];
while (j <= h)
b[k++] = arr[j++];
for (int i = 0; i < len; i++)
arr[l + i] = b[i];
}
Note however that a better approach to this problem is to save only the left half of the slice to merge and to consider h as the index of the first element after the end of the slice. This avoid confusing and error prone +1/-1 adjustments and reduces the number of copies:
void merge(int arr[], int low, int mid, int hi) {
int len1 = mid - lo;
int b[len1];
int i, j, k;
for (i = 0; i < len1; i++)
b[i] = a[low + i];
for (i = 0, j = mid, k = lo; i < len;) {
if (j >= hi || arr[i] <= arr[j])
arr[k++] = b[i++];
else
arr[k++] = arr[j++];
}
}
void Rmerge_sort(int arr[], int low, int hi) {
if (hi - low > 1) {
int mid = low + (hi - low) / 2; // avoid arithmetic overflow
Rmerge_sort(arr, low, mid);
Rmerge_sort(arr, mid, hi);
merge(arr, low, mid, hi);
}
}
int main() {
int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 10 };
int n = sizeof(arr) / sizeof(arr[0]);
Rmerge_sort(arr, 0, n);
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
}

How do you represent an image in OpenCL

I have sample code but it completely leaves out what my (void*)should_be!
I setup a cl_image_desc, cl_image_format, buffer, origin, and region:
cl_image_desc desc;
desc.image_type = CL_MEM_OBJECT_IMAGE2D;
desc.image_width = width;
desc.image_height = height;
desc.image_depth = 0;
desc.image_array_size = 0;
desc.image_row_pitch = 0;
desc.image_slice_pitch = 0;
desc.num_mip_levels = 0;
desc.num_samples = 0;
desc.buffer = NULL;
cl_image_format format;
format.image_channel_order = CL_R;
format.image_channel_data_type = CL_FLOAT;
cl_mem bufferSourceImage = clCreateImage(context, CL_MEM_READ_ONLY, &format, &desc, NULL, NULL);
size_t origin[3] = {0, 0, 0};
size_t region[3] = {width, height,1};
In this next snippet sourceImage is a void pointer to my image. But what is my image? For every pixel there are r, g, b, a, x, and y values.
clEnqueueWriteImage(queue, bufferSourceImage, CL_TRUE, origin, region, 0, 0, sourceImage, 0, NULL, NULL);
How do I turn my image (a bunch of (r,g,b,a,x,y)'s) into a suitable array?
This is the kernel they provide:
__kernel void convolution(__read_only image2d_t sourceImage, __write_only image2d_t outputImage, int rows, int cols, __constant float* filter, int filterWidth, sampler_t sampler)
{
int column = get_global_id(0);
int row = get_global_id(1);
int halfWidth = (int)(filterWidth/2);
float4 sum = {0.0f, 0.0f, 0.0f, 0.0f};
int filterIdx = 0;
int2 coords;
for(int i = -halfWidth; i <= halfWidth; i++)
{
coords.y = row + i;
for(int i2 = -halfWidth; i2 <= halfWidth; i2++)
{
coords.x = column + i2;
float4 pixel;
pixel = read_imagef(sourceImage, sampler, coords);
sum.x += pixel.x * filter[filterIdx++];
}
}
if(myRow < rows && myCol < cols)
{
coords.x = column;
coords.y = row;
write_imagef(outputImage, coords, sum);
}
}
Set up the cl_image_format as you like and then you just have to follow that format what you selected. Currently your channel (R, G, B, A) data should be represented as "single precision floating-point value" - image_channel_data_type = CL_FLOAT, and you can take only one channel of those and feed it into the expected R channel (image_channel_order = CL_R).
Your kernel expect float:
float4 pixel;
pixel = read_imagef(sourceImage, sampler, coords);

drawing a cube with perspective

I am trying to draw the Mandelbox using my own ray marching on the CPU.
I have a width*height bitmap to render to.
For each pixel, I want to march towards the cube:
static float eye = 0.0f; eye = glm::clamp(eye+0.005f,0.0f,1.0f); // animate
const glm::mat4 projection = glm::perspective(35.0f, (float)width/height, 0.1f, 10.0f),
modelview = glm::lookAt(glm::vec3(cos(eye),sin(eye),-1),glm::vec3(0,0,0),glm::vec3(0,0,1));
const float epsilon = sqrt(1.0f/std::max(width,height))/2.0f;
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++) {
glm::vec3 p = glm::unProject(glm::vec3(x,y,0),modelview,projection,glm::vec4(0,0,width,height)),
dir = glm::unProject(glm::vec3(x,y,1),modelview,projection,glm::vec4(0,0,width,height))-p,
P0 = p;
//std::cout << x << "," << y << " " << p.x << "," << p.y << "," << p.z << " " << dir.x << "," << dir.y << "," << dir.z << std::endl;
float D = 0;
for(int i=0; i<MAX_ITER; i++) {
const float d = DE(p);
D += d;
if(d<epsilon) {
depth_bmp[y*width+x] = 255.0f/i;
break;
}
p = dir*D + P0;
}
}
}
My distance estimator function is a very literal translation and looks like this:
float DE(glm::vec3 p) {
const float Scale = -1.77f, fixedRadius2 = 1.0f, minRadius2 = (0.5f*0.5f);
const glm::vec3 p0 = p;
float dr = 1.0f;
for(int n = 0; n < 13; n++) {
// Reflect
p = (glm::clamp(p,-1.0f,1.0f) * 2.0f) - p;
// Sphere Inversion
const float r2 = glm::dot(p,p);
if(r2<minRadius2) {
const float t = (fixedRadius2/minRadius2);
p *= t;
dr *= t;
} else if(r2<fixedRadius2) {
const float t = (fixedRadius2/r2);
p *= t;
dr *= t;
}
// Scale & Translate
p = p * Scale + p0;
dr = dr * abs(Scale) + 1.0f;
}
return glm::length(p)/abs(dr);
}
And the output looks completely unbox-like:
How do I set the eye transform up so I see the cube properly?
The issue is that the length of the ray must be normalised:
glm::vec3 p = glm::unProject(glm::vec3(x,y,0),modelview,projection,glm::vec4(0,0,width,height)),
dir = glm::unProject(glm::vec3(x,y,1),modelview,projection,glm::vec4(0,0,width,height))-p;
const float len = glm::length(dir);
dir = glm::normalise(dir);
float D = 0;
for(int i=0; i<MAX_ITER; i++) {
const float d = DE(p + dir*D);
D += d;
if(D > len) break;
...
You can use the method outlined here to generate the correct rays (and their lengths).

Resources