added working jpeg
This commit is contained in:
+17
-5
@@ -3,11 +3,23 @@ set(CMAKE_C_STANDARD 23)
|
|||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
project(ppm_image_processor LANGUAGES C)
|
project(image_processor LANGUAGES C)
|
||||||
find_package(JPEG REQUIRED)
|
# find_package(JPEG REQUIRED)
|
||||||
|
#
|
||||||
|
# add_executable(image_processor src/main.c)
|
||||||
|
#
|
||||||
|
# target_include_directories(image_processor PRIVATE ${JPEG_INCLUDE_DIRS})
|
||||||
|
#
|
||||||
|
# target_link_libraries(image_processor PRIVATE ${JPEG_LIBRARIES})
|
||||||
|
|
||||||
add_executable(ppm_image_processor src/main.c)
|
find_library(TURBOJPEG_LIB NAMES turbojpeg REQUIRED)
|
||||||
|
find_path(TURBOJPEG_INCLUDE NAMES turbojpeg.h REQUIRED)
|
||||||
|
|
||||||
target_include_directories(ppm_image_processor PRIVATE ${JPEG_INCLUDE_DIRS})
|
add_executable(image_processor src/main.c)
|
||||||
|
|
||||||
|
# 2. Include the directories for the header files
|
||||||
|
target_include_directories(image_processor PRIVATE ${TURBOJPEG_INCLUDE})
|
||||||
|
|
||||||
|
# 3. Link the actual turbojpeg library
|
||||||
|
target_link_libraries(image_processor PRIVATE ${TURBOJPEG_LIB})
|
||||||
|
|
||||||
target_link_libraries(ppm_image_processor PRIVATE ${JPEG_LIBRARIES})
|
|
||||||
|
|||||||
+185
-22
@@ -3,6 +3,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <turbojpeg.h>
|
||||||
|
|
||||||
#define BYTE_A 0xFF000000
|
#define BYTE_A 0xFF000000
|
||||||
#define BYTE_R 0x00FF0000
|
#define BYTE_R 0x00FF0000
|
||||||
@@ -35,7 +36,76 @@ typedef struct
|
|||||||
int width, height, len;
|
int width, height, len;
|
||||||
const char* name;
|
const char* name;
|
||||||
char fmt[3];
|
char fmt[3];
|
||||||
} Image;
|
} PPM;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int width, height;
|
||||||
|
size_t len;
|
||||||
|
const char* name;
|
||||||
|
} JPEG;
|
||||||
|
|
||||||
|
unsigned int*
|
||||||
|
jpeg_decode(JPEG* attr)
|
||||||
|
{
|
||||||
|
printf("Starting Decode...\n");
|
||||||
|
tjhandle tjdecompress = tj3Init(TJINIT_DECOMPRESS);
|
||||||
|
unsigned char* jpeg_buf = NULL;
|
||||||
|
FILE* infile = fopen(attr->name, "rb");
|
||||||
|
if (!infile) return NULL;
|
||||||
|
fseek(infile, 0, SEEK_END);
|
||||||
|
attr->len = ftell(infile);
|
||||||
|
fseek(infile, 0, SEEK_SET);
|
||||||
|
jpeg_buf = (unsigned char*)malloc(attr->len);
|
||||||
|
fread(jpeg_buf, 1, attr->len, infile);
|
||||||
|
fclose(infile);
|
||||||
|
if (tj3DecompressHeader(tjdecompress, jpeg_buf, attr->len) < 0)
|
||||||
|
{
|
||||||
|
printf("Header error: %s\n", tj3GetErrorStr(tjdecompress));
|
||||||
|
free(jpeg_buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
attr->width = tj3Get(tjdecompress, TJPARAM_JPEGWIDTH);
|
||||||
|
attr->height = tj3Get(tjdecompress, TJPARAM_JPEGHEIGHT);
|
||||||
|
unsigned int* pixels =
|
||||||
|
malloc(attr->width * attr->height * sizeof(unsigned int));
|
||||||
|
tj3Decompress8(tjdecompress, jpeg_buf, attr->len,
|
||||||
|
(unsigned char*)pixels, 0, TJPF_BGRA);
|
||||||
|
tj3Destroy(tjdecompress);
|
||||||
|
free(jpeg_buf);
|
||||||
|
printf("Decode Successful!\n");
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
jpeg_encode(unsigned int* src_buf, JPEG* attr, char* name)
|
||||||
|
{
|
||||||
|
printf("Starting Decode...\n");
|
||||||
|
tjhandle tjcompress = tj3Init(TJINIT_COMPRESS);
|
||||||
|
unsigned char* dst_buf = NULL;
|
||||||
|
size_t dst_size = 0;
|
||||||
|
tj3Set(tjcompress, TJPARAM_QUALITY, 80);
|
||||||
|
tj3Set(tjcompress, TJPARAM_SUBSAMP, TJSAMP_420);
|
||||||
|
if (tj3Compress8(tjcompress, (unsigned char*)src_buf, attr->width, 0,
|
||||||
|
attr->height, TJPF_BGRA, &dst_buf, &dst_size) < 0)
|
||||||
|
{
|
||||||
|
printf("TurboJPEG Error: %s\n", tj3GetErrorStr(tjcompress));
|
||||||
|
}
|
||||||
|
FILE* fptr = fopen(name, "wb");
|
||||||
|
if (!fptr || dst_buf == NULL || dst_size <= 0)
|
||||||
|
{
|
||||||
|
free(src_buf);
|
||||||
|
tj3Free(dst_buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fwrite(dst_buf, 1, dst_size, fptr);
|
||||||
|
fclose(fptr);
|
||||||
|
|
||||||
|
tj3Destroy(tjcompress);
|
||||||
|
tj3Free(dst_buf);
|
||||||
|
printf("Encode Successful!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -118,7 +188,7 @@ apply_opacity(const unsigned clr_a, const unsigned int clr_b, const float ratio)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int*
|
static inline unsigned int*
|
||||||
ppm_encode(const char* file, Image* img)
|
ppm_encode(const char* file, PPM* img)
|
||||||
{
|
{
|
||||||
printf("Starting encode...\n");
|
printf("Starting encode...\n");
|
||||||
unsigned char red;
|
unsigned char red;
|
||||||
@@ -128,19 +198,23 @@ ppm_encode(const char* file, Image* img)
|
|||||||
int max_val;
|
int max_val;
|
||||||
FILE* fptr;
|
FILE* fptr;
|
||||||
fptr = fopen(file, "r");
|
fptr = fopen(file, "r");
|
||||||
if (!fptr) return nullptr;
|
if (!fptr)
|
||||||
|
{
|
||||||
|
printf("Invalid file name: %s", file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
fscanf(fptr, "%s", fmt);
|
fscanf(fptr, "%s", fmt);
|
||||||
fscanf(fptr, "%d %d", &img->width, &img->height);
|
fscanf(fptr, "%d %d", &img->width, &img->height);
|
||||||
img->len = img->width * img->height;
|
img->len = img->width * img->height;
|
||||||
fscanf(fptr, "%d", &max_val);
|
fscanf(fptr, "%d", &max_val);
|
||||||
fgetc(fptr);
|
fgetc(fptr);
|
||||||
unsigned char* img_buf = malloc(img->len * 3);
|
unsigned char* img_buf = malloc(img->len * 3);
|
||||||
if (!img_buf) return nullptr;
|
if (!img_buf) return NULL;
|
||||||
unsigned int* clr_buf = malloc(img->len * sizeof(unsigned int));
|
unsigned int* clr_buf = malloc(img->len * sizeof(unsigned int));
|
||||||
if (!clr_buf)
|
if (!clr_buf)
|
||||||
{
|
{
|
||||||
free(img_buf);
|
free(img_buf);
|
||||||
return nullptr;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (fmt[1] == '3')
|
if (fmt[1] == '3')
|
||||||
{
|
{
|
||||||
@@ -170,7 +244,7 @@ ppm_encode(const char* file, Image* img)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
ppm_apply_brightness(unsigned int* buf, const Image img, const float factor)
|
ppm_apply_brightness(unsigned int* buf, const PPM img, const float factor)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < img.len; i++)
|
for (int i = 0; i < img.len; i++)
|
||||||
{
|
{
|
||||||
@@ -179,7 +253,7 @@ ppm_apply_brightness(unsigned int* buf, const Image img, const float factor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
ppm_decode(const unsigned int* buf, const Image img)
|
ppm_decode(unsigned int* buf, const PPM img)
|
||||||
{
|
{
|
||||||
unsigned char red, green, blue;
|
unsigned char red, green, blue;
|
||||||
unsigned char* dst_buf = malloc(img.len * 3);
|
unsigned char* dst_buf = malloc(img.len * 3);
|
||||||
@@ -189,7 +263,11 @@ ppm_decode(const unsigned int* buf, const Image img)
|
|||||||
|
|
||||||
if (img.fmt[1] == '3') fptr = fopen(img.name, "w");
|
if (img.fmt[1] == '3') fptr = fopen(img.name, "w");
|
||||||
if (img.fmt[1] == '6') fptr = fopen(img.name, "wb");
|
if (img.fmt[1] == '6') fptr = fopen(img.name, "wb");
|
||||||
if (!fptr) return;
|
if (!fptr)
|
||||||
|
{
|
||||||
|
free(dst_buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(fptr, "%s\n%d %d\n255\n", img.fmt, img.width, img.height);
|
fprintf(fptr, "%s\n%d %d\n255\n", img.fmt, img.width, img.height);
|
||||||
if (img.fmt[1] == '3')
|
if (img.fmt[1] == '3')
|
||||||
@@ -218,6 +296,7 @@ ppm_decode(const unsigned int* buf, const Image img)
|
|||||||
}
|
}
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
free(dst_buf);
|
free(dst_buf);
|
||||||
|
free(buf);
|
||||||
printf("Decode Done!\n");
|
printf("Decode Done!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,10 +335,10 @@ chromatic_circular_shift(const unsigned int clr, const int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int*
|
static inline unsigned int*
|
||||||
horizontal_flip(unsigned int* buf, const Image img)
|
horizontal_flip(unsigned int* buf, const PPM img)
|
||||||
{
|
{
|
||||||
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
|
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
|
||||||
if (!flip) return nullptr;
|
if (!flip) return NULL;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (int i = 0; i < img.height; i++)
|
for (int i = 0; i < img.height; i++)
|
||||||
{
|
{
|
||||||
@@ -275,10 +354,10 @@ horizontal_flip(unsigned int* buf, const Image img)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int*
|
static inline unsigned int*
|
||||||
vertical_flip(unsigned int* buf, Image img)
|
vertical_flip(unsigned int* buf, PPM img)
|
||||||
{
|
{
|
||||||
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
|
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
|
||||||
if (!flip) return nullptr;
|
if (!flip) return NULL;
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (int i = img.height - 1; i >= 0; i--)
|
for (int i = img.height - 1; i >= 0; i--)
|
||||||
@@ -308,7 +387,7 @@ constrast_adjustment(unsigned int clr, float factor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int*
|
static inline unsigned int*
|
||||||
rotate(unsigned int* buf, Image* img, int len)
|
rotate(unsigned int* buf, PPM* img, int len)
|
||||||
{
|
{
|
||||||
unsigned int* mask = malloc(len * sizeof(unsigned int));
|
unsigned int* mask = malloc(len * sizeof(unsigned int));
|
||||||
int old_width = img->width;
|
int old_width = img->width;
|
||||||
@@ -368,15 +447,97 @@ get_pixel_safe(unsigned int* buf, int x, int y, int width, int height)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int*
|
static inline int
|
||||||
box_blur(unsigned int* src_buf, Image* img, int len)
|
add_pixel(unsigned int pixel, unsigned int* red, unsigned int* green,
|
||||||
|
unsigned int* blue)
|
||||||
{
|
{
|
||||||
unsigned int* dst_buf = malloc(len * sizeof(unsigned int));
|
if (pixel != 0)
|
||||||
// Loop every pixel
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
|
*red += decode_r(pixel);
|
||||||
|
*green += decode_g(pixel);
|
||||||
|
*blue += decode_b(pixel);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int*
|
||||||
|
box_blur(unsigned int* src_buf, PPM* img, int radius)
|
||||||
|
{
|
||||||
|
unsigned int* tmp_buf = malloc(img->len * sizeof(unsigned int));
|
||||||
|
if (!tmp_buf) return 0;
|
||||||
|
unsigned int* dst_buf = malloc(img->len * sizeof(unsigned int));
|
||||||
|
if (!dst_buf)
|
||||||
|
{
|
||||||
|
free(tmp_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
printf("Pass 1: ...\n");
|
||||||
|
for (int y = 0; y < img->height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < img->width; x++)
|
||||||
|
{
|
||||||
|
unsigned long cpi = 0, red = 0, green = 0, blue = 0;
|
||||||
|
int count = 0;
|
||||||
|
int edge = (x < radius || x >= img->width - radius);
|
||||||
|
if (edge) continue;
|
||||||
|
cpi = src_buf[y * img->width + x];
|
||||||
|
|
||||||
|
for (int dx = -radius; dx <= radius; dx++)
|
||||||
|
{
|
||||||
|
int px = x + dx;
|
||||||
|
if (px < 0 || px >= img->width) continue;
|
||||||
|
unsigned int p = src_buf[y * img->width + px];
|
||||||
|
red += decode_r(p);
|
||||||
|
green += decode_g(p);
|
||||||
|
blue += decode_b(p);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
tmp_buf[y * img->width + x] =
|
||||||
|
encode((red / count), (green / count),
|
||||||
|
(blue / count));
|
||||||
|
}
|
||||||
|
else tmp_buf[y * img->width + x] = cpi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Pass 1: Successful\n");
|
||||||
|
printf("Pass 2: ...\n");
|
||||||
|
for (int y = 0; y < img->height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < img->width; x++)
|
||||||
|
{
|
||||||
|
unsigned long cpi = 0, red = 0, green = 0, blue = 0;
|
||||||
|
int count = 0;
|
||||||
|
int edge = (y < radius || y >= img->height - radius);
|
||||||
|
if (edge) continue;
|
||||||
|
cpi = src_buf[y * img->width + x];
|
||||||
|
|
||||||
|
for (int dy = -radius; dy <= radius; dy++)
|
||||||
|
{
|
||||||
|
int py = y + dy;
|
||||||
|
if (py < 0 || py >= img->height)
|
||||||
|
continue;
|
||||||
|
unsigned int p = tmp_buf[py * img->width + x];
|
||||||
|
red += decode_r(p);
|
||||||
|
green += decode_g(p);
|
||||||
|
blue += decode_b(p);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
dst_buf[y * img->width + x] =
|
||||||
|
encode((red / count), (green / count),
|
||||||
|
(blue / count));
|
||||||
|
}
|
||||||
|
else dst_buf[y * img->width + x] = cpi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Pass 2: Successful\n");
|
||||||
|
free(tmp_buf);
|
||||||
free(src_buf);
|
free(src_buf);
|
||||||
return dst_buf;
|
return dst_buf;
|
||||||
}
|
}
|
||||||
@@ -384,17 +545,19 @@ box_blur(unsigned int* src_buf, Image* img, int len)
|
|||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
Image img = { .width = 3,
|
PPM img = { .width = 3,
|
||||||
.height = 2,
|
.height = 2,
|
||||||
.name = "../color.ppm",
|
.name = "../color.ppm",
|
||||||
.fmt = "P6",
|
.fmt = "P6",
|
||||||
.len = 0 };
|
.len = 0 };
|
||||||
|
|
||||||
unsigned int* buf = ppm_encode("../assets/wtest_p6.ppm.ppm", &img);
|
unsigned int* buf = ppm_encode("../assets/8ktest.ppm", &img);
|
||||||
if (buf)
|
if (buf)
|
||||||
{
|
{
|
||||||
|
printf("Applying Filter...\n");
|
||||||
|
buf = box_blur(buf, &img, 40);
|
||||||
|
printf("Finished Applying Filter!\n");
|
||||||
ppm_decode(buf, img);
|
ppm_decode(buf, img);
|
||||||
free(buf);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user