Browse Source

Prototype completed

master
Felix 4 years ago
parent
commit
c9c87eab0e
  1. 2
      Makefile
  2. 35
      infragram.cpp
  3. 127
      jpeg.hpp
  4. 84
      ndvi.hpp

2
Makefile

@ -28,5 +28,5 @@ clean:
%.o: %.cpp %.hpp
$(CXX) $(CXX_FLAGS) -c $(INCLUDE) -o $@ $< $(LIBS)
infragram: infragram.cpp jpeg.hpp
infragram: infragram.cpp jpeg.hpp ndvi.hpp
$(CXX) $(CXX_FLAGS) $(INCLUDE) -o $@ $< $(LIBS)

35
infragram.cpp

@ -21,6 +21,7 @@
// Jpeg reading routines
#include "jpeg.hpp"
#include "ndvi.hpp"
using namespace std;
@ -181,10 +182,13 @@ static int read_video(const char* device, int width, int height, const char* jpg
int main() {
int main(int argc, char** argv) {
const char* device = "/dev/video0";
const char* filename = "webcam_output.jpeg";
if (argc > 1) device = argv[1];
if (argc > 2) filename = argv[2];
::unlink(filename);
int ret = read_video(device, 1900, 600, filename);
@ -196,35 +200,10 @@ int main() {
try {
Jpeg jpeg(filename);
// Processing image using NDVI analysis
// Infragram: red channel = infrared, blue channel = visible
// NDVI = (IR-RGB)/(IR+RGB) where IR is the near IR and RGB is the visible light
// So for each pixel we are doing (R-B)/(R+B)
const int width = jpeg.width;
const int height= jpeg.height;
Jpeg dest(width, height);
for(int x=0;x<width;x++) {
for(int y=0;y<height;y++) {
rgb_t rgb = jpeg.rgb(x,y);
const unsigned char ir = rgb.r;
const unsigned char vis = rgb.b;
const float ndvi = (float)(ir-vis)/(float)(ir+vis);
//cout << (int)ir << '\t' << (int)vis << '\t' << ndvi << endl;
unsigned char x = (unsigned char)(ndvi * 255.0);
if(x > 255) x = 255;
//rgb.r = x; rgb.g = x; rgb.b = x;
dest.set(x,y, rgb);
}
}
Jpeg j_ndvi = ndvi(jpeg);
::unlink("output.jpeg");
dest.write("output.jpeg");
j_ndvi.write("output.jpeg");
cout << "NDVI written to 'output.jpeg'" << endl;
} catch (const char* err) {

127
jpeg.hpp

@ -3,6 +3,9 @@
* Do not mind the crappy quality
*/
#ifndef _INFRAGRAM_JPEG_HPP
#define _INFRAGRAM_JPEG_HPP
#include <stdio.h>
#include <unistd.h>
#include <jpeglib.h>
@ -11,6 +14,7 @@
#include <iostream>
using namespace std;
struct {
unsigned char r;
@ -36,6 +40,15 @@ public:
this->bmap = new unsigned char[size];
memcpy(this->bmap, src.bmap, sizeof(unsigned char)*size);
}
Jpeg(Jpeg &&src) {
this->width = src.width;
this->height = src.height;
this->depth = src.depth;
this->bmap = src.bmap;
src.width = src.height = src.depth = 0;
src.bmap = NULL;
}
Jpeg(const int width, const int height) {
this->width = width;
this->height = height;
@ -67,10 +80,10 @@ public:
height = cinfo.output_height;
unsigned char * pDummy = new unsigned char [width*height*3];
unsigned char * pTest = pDummy;
if (!pDummy) {
if (!pDummy)
throw "Out of memory";
}
unsigned char * pTest = pDummy;
row_stride = width * cinfo.output_components;
pJpegBuffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
@ -99,45 +112,46 @@ public:
depth = 32;
}
void write(const char* filename, int quality=100) {
void write(const char* filename, int quality=100) const {
FILE *ofp;
struct jpeg_compress_struct cinfo; /* JPEG compression struct */
struct jpeg_error_mgr jerr; /* JPEG error handler */
JSAMPROW row_pointer[1]; /* output row buffer */
int row_stride; /* physical row width in output buf */
if ((ofp = fopen(filename, "wb")) == NULL)
throw "Error opening file";
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, ofp);
cinfo.image_width = this->width;
cinfo.image_height = this->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, 0);
jpeg_start_compress(&cinfo, TRUE);
struct jpeg_compress_struct cinfo; /* JPEG compression struct */
struct jpeg_error_mgr jerr; /* JPEG error handler */
JSAMPROW row_pointer[1]; /* output row buffer */
int row_stride; /* physical row width in output buf */
if ((ofp = fopen(filename, "wb")) == NULL)
throw "Error opening file";
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, ofp);
cinfo.image_width = this->width;
cinfo.image_height = this->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, 0);
jpeg_start_compress(&cinfo, TRUE);
/* Calculate the size of a row in the image */
row_stride = cinfo.image_width * cinfo.input_components;
/* Calculate the size of a row in the image */
row_stride = cinfo.image_width * cinfo.input_components;
/* compress the JPEG, one scanline at a time into the buffer */
while (cinfo.next_scanline < cinfo.image_height) {
//cout << "Write cinfo.next_scanline = " << cinfo.next_scanline << endl;
//row_pointer[0] = &(bmap[(cinfo.image_height - cinfo.next_scanline - 1)*row_stride]);
row_pointer[0] = &(bmap[(cinfo.next_scanline)*row_stride]);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
/* compress the JPEG, one scanline at a time into the buffer */
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &(bmap[(this->height - cinfo.next_scanline - 1)*row_stride]);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(ofp);
fclose(ofp);
// Everything good so far :-)
// Everything good so far :-)
}
virtual ~Jpeg() {
@ -147,10 +161,10 @@ public:
}
}
rgb_t operator()(int x, int y) {
rgb_t operator()(int x, int y) const {
return this->rgb(x,y);
}
rgb_t rgb(int x, int y) {
rgb_t rgb(int x, int y) const {
//if(bmap == NULL) throw "No data";
rgb_t ret;
@ -171,39 +185,4 @@ public:
};
#if 0
using namespace std;
int main() {
try {
Jpeg jpeg("webcam_output.jpeg");
cout << "image read (" << jpeg.width << "x" << jpeg.height << ")" << endl;
rgb_t rgb;
rgb = jpeg(0,0);
cout << "(0,0) = " << (int)rgb.r << ',' << (int)rgb.g << ',' << (int)rgb.b << endl;
rgb = jpeg(0,1);
cout << "(0,1) = " << (int)rgb.r << ',' << (int)rgb.g << ',' << (int)rgb.b << endl;
rgb = jpeg(1,0);
cout << "(1,0) = " << (int)rgb.r << ',' << (int)rgb.g << ',' << (int)rgb.b << endl;
rgb = jpeg(1,1);
cout << "(1,1) = " << (int)rgb.r << ',' << (int)rgb.g << ',' << (int)rgb.b << endl;
rgb = jpeg(824,432);
cout << "(824,432) = " << (int)rgb.r << ',' << (int)rgb.g << ',' << (int)rgb.b << endl;
} catch (const char* msg) {
cerr << "Error: " << msg << endl;
return EXIT_FAILURE;
} catch (...) {
cerr << "Unknown error" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#endif

84
ndvi.hpp

@ -0,0 +1,84 @@
/** 2018, Felix Niederwanger
* This is my own implementation of the NDVI analysis according to https://publiclab.org/wiki/ndvi
*/
#ifndef _INFRAGRAM_NDVI_HPP_
#define _INFRAGRAM_NDVI_HPP_
#include <math.h>
#include "jpeg.hpp"
float min(float* array, size_t size) {
if(size == 0) return 0.0F;
float f_min = array[0];
for(size_t i=1;i<size;i++)
f_min = fminf(f_min, array[i]);
return f_min;
}
float max(float* array, size_t size) {
if(size == 0) return 0.0F;
float f_max = array[0];
for(size_t i=1;i<size;i++)
f_max = fmaxf(f_max, array[i]);
return f_max;
}
static rgb_t colormap(const float x, const float fmin, const float fmax) {
const float y = (x-fmin)/(fmax-fmin);
rgb_t rgb;
rgb.r = (1.0-y)*255;
rgb.g = y*255;
rgb.b = 0;
return rgb;
}
/** Create ndvi jpeg out of the given jpeg */
static Jpeg ndvi(const Jpeg &jpeg) {
// Processing image using NDVI analysis
// Infragram: red channel = infrared, blue channel = visible
// NDVI = (IR-RGB)/(IR+RGB) where IR is the near IR and RGB is the visible light
// So for each pixel we are doing (R-B)/(R+B)
const int width = jpeg.width;
const int height= jpeg.height;
const size_t size = width*height;
Jpeg dest(width, height);
float buffer[size];
rgb_t rgb;
int i=0;
for(int x=0;x<width;x++) {
for(int y=0;y<height;y++) {
rgb = jpeg.rgb(x,y);
const unsigned char ir = rgb.r;
const unsigned char vis = rgb.b;
buffer[i++] = (float)(ir-vis)/(float)(ir+vis);
}
}
// Apply colormap
const float fmin = min(buffer, size);
const float fmax = max(buffer, size);
i = 0;
for(int x=0;x<width;x++) {
for(int y=0;y<height;y++) {
rgb = colormap(buffer[i++], fmin, fmax);
dest.set(x,y, rgb);
}
}
return dest;
}
#endif
Loading…
Cancel
Save