Skip to content

Instantly share code, notes, and snippets.

@leonardorame
Created December 18, 2022 14:40
Show Gist options
  • Save leonardorame/f8a932663ec642aae7cc4e1ca7062ba6 to your computer and use it in GitHub Desktop.
Save leonardorame/f8a932663ec642aae7cc4e1ca7062ba6 to your computer and use it in GitHub Desktop.
//
// 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