Created
December 18, 2022 14:40
-
-
Save leonardorame/f8a932663ec642aae7cc4e1ca7062ba6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ssrlib.cc (Server Side Rendering library) | |
// | |
// Copyright (c) 2022 InformeMedico.com.ar | |
// | |
// | |
#include <erl_nif.h> | |
#include <png.h> | |
#include <vector> | |
#include <stdlib.h> | |
#include <string> | |
#include "dcmtk/dcmdata/dctk.h" /* for various dcmdata headers */ | |
#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ | |
#include "dcmtk/dcmjpeg/djdecode.h" /* for dcmjpeg decoders */ | |
#include "dcmtk/dcmjpeg/dipijpeg.h" /* for dcmimage JPEG plugin */ | |
#ifdef BUILD_DCM2PNM_AS_DCML2PNM | |
#include "dcmtk/dcmjpls/djdecode.h" /* for dcmjpls decoders */ | |
#endif | |
#include "dcmtk/dcmimage/dipipng.h" /* for dcmimage PNG plugin */ | |
#include "dcmtk/dcmdata/dcfilefo.h" /* for dcmimage PNG plugin */ | |
#include "dcmtk/dcmdata/dcrledrg.h" /* for DcmRLEDecoderRegistration */ | |
#include "dcmtk/dcmimgle/dcmimage.h" /* for DicomImage */ | |
#include "dcmtk/dcmimgle/digsdfn.h" /* for DiGSDFunction */ | |
#include "dcmtk/dcmimgle/diciefn.h" /* for DiCIELABFunction */ | |
#include "dcmtk/dcmimage/diregist.h" /* include to support color images */ | |
#include "dcmtk/ofstd/ofstd.h" /* for OFStandard */ | |
#include "dcmtk/dcmjpeg/djdecode.h" /* for dcmjpeg decoders */ | |
#include "dcmtk/dcmjpeg/dipijpeg.h" /* for dcmimage JPEG plugin */ | |
#include "dcmtk/dcmdata/dcjson.h" /* for json output */ | |
#include "dcmtk/dcmdata/dcvr.h" /* for json output */ | |
#ifdef BUILD_DCM2PNM_AS_DCML2PNM | |
#include "dcmtk/dcmjpls/djdecode.h" /* for dcmjpls decoders */ | |
#endif | |
#include "dcmtk/dcmimage/dipipng.h" /* for dcmimage PNG plugin */ | |
#define INCLUDE_CSTDIO | |
#define INCLUDE_CSTRING | |
#define MAXBUFLEN 1024 | |
typedef unsigned char ui8; | |
static ErlNifResourceType* ff_res_t = NULL; | |
typedef struct { | |
DcmFileFormat * _ff; | |
DicomImage * _dicomimage; // reference to scaled dicom image | |
} dcmfileformat_t; | |
static void PNGWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) | |
{ | |
std::vector<ui8> *p = (std::vector<ui8> *)png_get_io_ptr(png_ptr); | |
p->insert(p->end(), data, data + length); | |
} | |
struct BulkDataURIJsonFormat : DcmJsonFormatCompact | |
{ | |
BulkDataURIJsonFormat(const OFBool printMetaheaderInformation = OFTrue) | |
: DcmJsonFormatCompact(printMetaheaderInformation) | |
{ | |
} | |
virtual OFBool asBulkDataURI(const DcmTagKey& tag, OFString& uri) | |
{ | |
if (DcmTag(tag).getEVR() == EVR_OB | |
|| DcmTag(tag).getEVR() == EVR_OW | |
|| DcmTag(tag).getEVR() == EVR_ox | |
) | |
{ | |
uri = "<<BINARY_DATA_OMITTED>>"; | |
return OFTrue; | |
} else if(tag.isPrivate()) { | |
uri = "<<PRIVATE>>"; | |
return OFTrue; | |
}; | |
return OFFalse; | |
} | |
}; | |
int writePNGtoMemory( | |
DicomImage *image, | |
std::vector<ui8> *out, | |
const unsigned long frame) | |
{ | |
volatile int result = 0; // gcc -W requires volatile here because of longjmp | |
if ((image != NULL) && (out != NULL)) | |
{ | |
/* create bitmap with 8 or 16 bits per sample */ | |
const int bit_depth = 8; //bitsPerSample; | |
const DiPNGInterlace interlaceType = E_pngInterlaceNone; | |
const DiPNGMetainfo metainfoType = E_pngFileMetainfo; | |
//const void *data = image->getOutputData(frame, bit_depth /*bits*/, 0 /*planar*/); | |
const void *data = image->getOutputData(8); | |
if (data != NULL) | |
{ | |
png_struct *png_ptr = NULL; | |
png_info *info_ptr = NULL; | |
png_byte *pix_ptr = NULL; | |
png_byte **volatile row_ptr = NULL; | |
volatile png_textp text_ptr = NULL; | |
png_time ptime; | |
const int width = image->getWidth(); | |
const int height = image->getHeight(); | |
int color_type; | |
int bpp; // bytesperpixel | |
int row; | |
// create png write struct | |
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if (png_ptr == NULL) | |
{ | |
return 0; | |
} | |
// create png info struct | |
info_ptr = png_create_info_struct(png_ptr); | |
if (info_ptr == NULL) | |
{ | |
png_destroy_write_struct(&png_ptr, NULL); | |
return 0; | |
} | |
// setjmp stuff for png lib | |
if (setjmp(png_jmpbuf(png_ptr))) | |
{ | |
png_destroy_write_struct(&png_ptr, NULL); | |
if (row_ptr) | |
delete[] row_ptr; | |
if (text_ptr) | |
delete[] text_ptr; | |
return 0; | |
} | |
if ((image->getPhotometricInterpretation() == EPI_Monochrome1) || | |
(image->getPhotometricInterpretation() == EPI_Monochrome2)) | |
{ | |
color_type = PNG_COLOR_TYPE_GRAY; | |
bpp = bit_depth / 8; | |
} | |
else | |
{ | |
color_type = PNG_COLOR_TYPE_RGB; | |
bpp = 3 * bit_depth / 8; | |
} | |
int opt_interlace = E_pngInterlaceNone; | |
switch (interlaceType) | |
{ | |
case E_pngInterlaceAdam7: | |
opt_interlace = PNG_INTERLACE_ADAM7; | |
break; | |
case E_pngInterlaceNone: | |
opt_interlace = PNG_INTERLACE_NONE; | |
break; | |
} | |
//std::cout << "width: " << width << std::endl; | |
//std::cout << "height: " << height << std::endl; | |
//std::cout << "bit_depth: " << bit_depth << std::endl; | |
//std::cout << "color_type: " << color_type << std::endl; | |
//std::cout << "opt_interlace: " << opt_interlace << std::endl; | |
// set write mode | |
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, | |
opt_interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
// write header | |
row_ptr = new png_bytep[height]; | |
if (row_ptr == NULL) | |
{ | |
png_destroy_write_struct(&png_ptr, NULL); | |
if (text_ptr) | |
delete[] text_ptr; | |
return result; | |
} | |
for (row = 0, pix_ptr = OFstatic_cast(png_byte *, OFconst_cast(void *, data)); | |
row < height; | |
row++, pix_ptr += width * bpp) | |
{ | |
row_ptr[row] = pix_ptr; | |
} | |
// swap bytes (if needed) | |
if ((bit_depth == 16) && (gLocalByteOrder != EBO_BigEndian)) | |
png_set_swap(png_ptr); | |
// write image | |
out->clear(); | |
png_set_rows(png_ptr, info_ptr, row_ptr); | |
png_set_write_fn(png_ptr, out, PNGWriteCallback, NULL); | |
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); | |
// finish | |
png_destroy_write_struct(&png_ptr, NULL); | |
delete[] row_ptr; | |
if (text_ptr) | |
delete[] text_ptr; | |
result = 1; | |
} | |
} | |
return result; | |
} | |
DcmFileFormat * loadDicomFile(const char * dicomfile){ | |
DcmFileFormat *ff = new DcmFileFormat(); | |
if (ff->loadFile(dicomfile).good()) | |
{ | |
return ff; | |
} | |
return NULL; | |
} | |
// This is called everytime a resource is deallocated (which happens when | |
// enif_release_resource is called and Erlang garbage collects the memory) | |
void ff_res_destructor(ErlNifEnv *env, void *res) { | |
std::cout << "en ff_res_destructor" << std::endl; | |
DcmFileFormat **ff_res = (DcmFileFormat**) res; | |
delete *ff_res; | |
} | |
int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { | |
std::cout << "en load" << std::endl; | |
ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); | |
ff_res_t = | |
enif_open_resource_type(env, NULL, "ff", ff_res_destructor, flags, NULL); | |
return 0; | |
} | |
ERL_NIF_TERM loadDicomFile_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
char dicomfile[MAXBUFLEN]; | |
enif_get_string(env, argv[0], dicomfile, sizeof(dicomfile), ERL_NIF_LATIN1); | |
DcmFileFormat *ff = loadDicomFile(dicomfile); | |
// Let's allocate the memory for a DcmFileFormat * pointer | |
dcmfileformat_t *ff_res = (dcmfileformat_t *)enif_alloc_resource(ff_res_t, sizeof(dcmfileformat_t)); | |
if (ff_res == NULL){ | |
std::cout << "ff_res = NULL" << std::endl; | |
enif_make_badarg(env); | |
} | |
ff_res->_ff = ff; | |
// We can now make the Erlang term that holds the resource | |
ERL_NIF_TERM term = enif_make_resource(env, ff_res); | |
// ...and release the resource so that it will be freed when Erlang garbage collects | |
enif_release_resource(ff_res); | |
return term; | |
} | |
ERL_NIF_TERM getTagValue_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
dcmfileformat_t *ff_res; | |
int group; | |
int element; | |
if(!enif_get_resource(env, argv[0], ff_res_t, (void **) &ff_res)) { | |
return enif_make_badarg(env); | |
}; | |
if (!enif_get_int(env, argv[1], &group)) | |
return enif_make_badarg(env); | |
if (!enif_get_int(env, argv[2], &element)) | |
return enif_make_badarg(env); | |
DcmDataset *ds = ff_res->_ff->getDataset(); | |
OFString value; | |
DcmTagKey key(group,element); | |
ds->findAndGetOFString(key, value); | |
return enif_make_string(env, value.c_str(), ERL_NIF_LATIN1); | |
} | |
ERL_NIF_TERM getHeader_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
dcmfileformat_t *ff_res; | |
int group; | |
int element; | |
if(!enif_get_resource(env, argv[0], ff_res_t, (void **) &ff_res)) { | |
return enif_make_badarg(env); | |
}; | |
DcmDataset *ds = ff_res->_ff->getDataset(); | |
std::ostringstream jsonstream; | |
ds->writeJson(jsonstream, BulkDataURIJsonFormat(OFTrue)); | |
std::ostringstream res; | |
res << "{" << jsonstream.str() << "}"; | |
return enif_make_string(env, res.str().c_str(), ERL_NIF_LATIN1); | |
} | |
ERL_NIF_TERM createScaledImage_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
std::cout << "--0--" << std::endl; | |
dcmfileformat_t *ff_res; | |
unsigned long width; | |
unsigned long height; | |
int interpolate; | |
int aspect; | |
std::cout << "--1--" << std::endl; | |
if(!enif_get_resource(env, argv[0], ff_res_t, (void **) &ff_res)) { | |
return enif_make_badarg(env); | |
}; | |
std::cout << "--2--" << std::endl; | |
if (!enif_get_ulong(env, argv[1], &width)) | |
return enif_make_badarg(env); | |
std::cout << "--3--" << std::endl; | |
if (!enif_get_ulong(env, argv[2], &height)) | |
return enif_make_badarg(env); | |
std::cout << "--4--" << std::endl; | |
if (!enif_get_int(env, argv[3], &interpolate)) | |
return enif_make_badarg(env); | |
std::cout << "--5--" << std::endl; | |
if (!enif_get_int(env, argv[4], &aspect)) | |
return enif_make_badarg(env); | |
std::cout << "--6--" << std::endl; | |
DcmDataset *ds = ff_res->_ff->getDataset(); | |
std::cout << "--7--" << std::endl; | |
DicomImage * dicomImage = new DicomImage(ds, EXS_Unknown); | |
std::cout << "--8--" << std::endl; | |
ff_res->_dicomimage = dicomImage->createScaledImage(width, height, interpolate, aspect); | |
delete dicomImage; | |
std::cout << "en createScaledImage_nif" << std::endl; | |
return enif_make_atom(env, "ok"); | |
} | |
ERL_NIF_TERM setMinMaxWindow_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
dcmfileformat_t *ff_res; | |
int group; | |
int element; | |
if(!enif_get_resource(env, argv[0], ff_res_t, (void **) &ff_res)) { | |
return enif_make_badarg(env); | |
}; | |
ff_res->_dicomimage->setMinMaxWindow(0); | |
return enif_make_atom(env, "ok"); | |
} | |
ERL_NIF_TERM getPNG_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
dcmfileformat_t *ff_res; | |
if(!enif_get_resource(env, argv[0], ff_res_t, (void **) &ff_res)) { | |
return enif_make_badarg(env); | |
}; | |
DcmDataset *ds = ff_res->_ff->getDataset(); | |
std::vector<ui8> out; | |
writePNGtoMemory(ff_res->_dicomimage, &out, 0); | |
ErlNifBinary bin; | |
enif_alloc_binary(out.size(), &bin); | |
memcpy(bin.data, out.data(), out.size()); | |
return enif_make_binary(env, &bin); | |
return enif_make_atom(env, "ok"); | |
} | |
ERL_NIF_TERM initialize_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
DJDecoderRegistration::registerCodecs(); | |
return enif_make_atom(env, "ok"); | |
} | |
ERL_NIF_TERM finalize_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) | |
{ | |
DJDecoderRegistration::cleanup(); | |
return enif_make_atom(env, "ok"); | |
} | |
static int upgrade(ErlNifEnv* caller_env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) { | |
return 0; | |
} | |
ErlNifFunc nif_funcs[] = | |
{ | |
{"_initialize", 0, initialize_nif}, | |
{"_finalize", 0, finalize_nif}, | |
{"_getPNG", 1, getPNG_nif}, | |
{"_setMinMaxWindow", 1, setMinMaxWindow_nif}, | |
{"_loadDicomFile", 1, loadDicomFile_nif}, | |
{"_getTagValue", 3, getTagValue_nif}, | |
{"_createScaledImage", 5, createScaledImage_nif}, | |
{"_getHeader", 1, getHeader_nif}, | |
}; | |
ERL_NIF_INIT(Elixir.Dcmtknif, nif_funcs, &load, | |
nullptr, upgrade, nullptr); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment