├── .gitignore ├── .travis.yml ├── appveyor.yml ├── include └── fmjpeg2k │ ├── memory_file.h │ ├── dldefine.h │ ├── djerror.h │ ├── djdecode.h │ ├── djrparam.h │ ├── djencode.h │ ├── djlsutil.h │ ├── djcparam.h │ ├── djcodecd.h │ └── djcodece.h ├── .github └── workflows │ └── cmake.yml ├── README.md ├── cmake └── fmjpeg2kConfig.cmake.in ├── djdecode.cc ├── djrparam.cc ├── djutils.cc ├── djencode.cc ├── djcparam.cc ├── CMakeLists.txt ├── memory_file.cpp ├── LICENSE ├── dcmdjp2k.cc ├── dcmcjp2k.cc ├── djcodecd.cc └── djcodece.cc /.gitignore: -------------------------------------------------------------------------------- 1 | build-Debug/ 2 | build-Release/ 3 | Debug/ 4 | Release/ 5 | dcmtk/ 6 | openjpeg/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | 5 | os: 6 | - osx 7 | 8 | # use containers 9 | sudo: false 10 | 11 | script: 12 | - cd $TRAVIS_BUILD_DIR 13 | - ./build-osx.sh 14 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | configuration: 4 | - Release 5 | 6 | build_script: 7 | - cd %APPVEYOR_BUILD_FOLDER% 8 | - build.bat 9 | 10 | after_build: 11 | - cd %APPVEYOR_BUILD_FOLDER% 12 | - 7z a fmjpeg2koj-win.7z %TYPE% 13 | 14 | artifacts: 15 | - path: fmjpeg2koj-win.7z 16 | name: fmjpeg2koj-win -------------------------------------------------------------------------------- /include/fmjpeg2k/memory_file.h: -------------------------------------------------------------------------------- 1 | #include "openjpeg.h" 2 | 3 | struct DecodeData { 4 | public: 5 | DecodeData(unsigned char* src_data, OPJ_SIZE_T src_size) : 6 | src_data(src_data), src_size(src_size), offset(0) { 7 | } 8 | unsigned char* src_data; 9 | OPJ_SIZE_T src_size; 10 | OPJ_SIZE_T offset; 11 | }; 12 | 13 | opj_stream_t* OPJ_CALLCONV opj_stream_create_memory_stream(DecodeData* p_mem, OPJ_UINT32 p_size, bool p_is_read_stream); 14 | void msg_callback(const char* msg, void* f); -------------------------------------------------------------------------------- /include/fmjpeg2k/dldefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: Contains preprocessor definitions 15 | * 16 | */ 17 | 18 | 19 | #ifndef DL2KDEFINE_H 20 | #define DL2KDEFINE_H 21 | 22 | #include "dcmtk/config/osconfig.h" 23 | 24 | #include "dcmtk/ofstd/ofdefine.h" 25 | 26 | 27 | #ifdef fmjpeg2k_EXPORTS 28 | #define FMJPEG2K_EXPORT DCMTK_DECL_EXPORT 29 | #else 30 | #define FMJPEG2K_EXPORT DCMTK_DECL_IMPORT 31 | #endif 32 | 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Build 24 | # Build your program with the given configuration 25 | run: ./build.sh ${{env.BUILD_TYPE}} 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fmjpeg2koj 2 | Windows [![Build status](https://ci.appveyor.com/api/projects/status/mk6procjyjwbed2d?svg=true)](https://ci.appveyor.com/project/DraconPern/fmjpeg2koj) OS X [![Build Status](https://app.travis-ci.com/DraconPern/fmjpeg2koj.svg?branch=master)](https://app.travis-ci.com/DraconPern/fmjpeg2koj) Ubuntu [![CMake](https://github.com/DraconPern/fmjpeg2koj/actions/workflows/cmake.yml/badge.svg)](https://github.com/DraconPern/fmjpeg2koj/actions/workflows/cmake.yml) 3 | 4 | JPEG2000 codec for DCMTK based on openjpeg 5 | 6 | ## Download 7 | Source https://github.com/DraconPern/fmjpeg2koj 8 | 9 | ## Requirements 10 | - CMake http://www.cmake.org/download/ 11 | 12 | ## Third party dependency 13 | - DCMTK http://dicom.offis.de/ 14 | - openjpeg http://www.openjpeg.org/ 15 | 16 | ## Author 17 | Ing-Long Eric Kuo 18 | 19 | ## License 20 | Copyright 2015-2021 Ing-Long Eric Kuo 21 | 22 | Licensed under the Apache License, Version 2.0 (the "License"); 23 | you may not use this file except in compliance with the License. 24 | You may obtain a copy of the License at 25 | 26 | http://www.apache.org/licenses/LICENSE-2.0 27 | 28 | Unless required by applicable law or agreed to in writing, software 29 | distributed under the License is distributed on an "AS IS" BASIS, 30 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | See the License for the specific language governing permissions and 32 | limitations under the License. 33 | 34 | -------------------------------------------------------------------------------- /cmake/fmjpeg2kConfig.cmake.in: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # fmjpeg2kConfig.cmake - CMake configuration file for external projects. 4 | # 5 | # This file is configured by fmjpeg2k and used by the Usefmjpeg2k.cmake 6 | # module to load fmjpeg2k's settings for an external project. 7 | @fmjpeg2k_CONFIG_INSTALL_ONLY@ 8 | # The fmjpeg2k version number. 9 | #set(fmjpeg2k_MAJOR_VERSION "@fmjpeg2k_VERSION_MAJOR@") 10 | #set(fmjpeg2k_MINOR_VERSION "@fmjpeg2k_VERSION_MINOR@") 11 | #set(fmjpeg2k_BUILD_VERSION "@fmjpeg2k_VERSION_BUILD@") 12 | 13 | set(FMJPEG2K_LIBRARIES fmjpeg2k) 14 | 15 | # The configuration options. 16 | set(fmjpeg2k_BUILD_SHARED_LIBS "@BUILD_SHARED_LIBS@") 17 | 18 | get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 19 | if(EXISTS ${SELF_DIR}/fmjpeg2kTargets.cmake) 20 | # This is an install tree 21 | include(${SELF_DIR}/fmjpeg2kTargets.cmake) 22 | get_filename_component(fmjpeg2k_INCLUDE_ROOT "${SELF_DIR}/../../../include" ABSOLUTE) 23 | set(FMJPEG2K_INCLUDE_DIRS ${fmjpeg2k_INCLUDE_ROOT}) 24 | else() 25 | if(EXISTS ${SELF_DIR}/fmjpeg2kExports.cmake) 26 | # This is a build tree 27 | set( FMJPEG2K_INCLUDE_DIRS "@CMAKE_SOURCE_DIR@/include") 28 | 29 | include(${SELF_DIR}/fmjpeg2kExports.cmake) 30 | 31 | else() 32 | message(FATAL_ERROR "ooops") 33 | endif() 34 | endif() 35 | 36 | # Backward compatible part: 37 | set(fmjpeg2k_FOUND TRUE) 38 | message(STATUS "Found fmjpeg2k: ${FMJPEG2K_LIBRARIES}") -------------------------------------------------------------------------------- /include/fmjpeg2k/djerror.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: Helper function than converts between CharLS and dcmjpgls errors 15 | * 16 | */ 17 | 18 | #ifndef DJERROR_H 19 | #define DJERROR_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "djlsutil.h" /* For the OFCondition codes */ 23 | #include "openjpeg.h" /* OpenJPEG include */ 24 | 25 | /** Helper class for converting between dcmjpls and CharLS error codes 26 | */ 27 | class DJLSError 28 | { 29 | private: 30 | /// private undefined constructor 31 | DJLSError(); 32 | 33 | public: 34 | 35 | /** This method converts a CharLS error code into a dcmjpls OFCondition 36 | * @param error The CharLS error code 37 | * @return The OFCondition 38 | */ 39 | static const OFConditionConst& convert(int error) 40 | { 41 | switch (error) 42 | { 43 | case 0: 44 | return EC_Normal; 45 | /* case UncompressedBufferTooSmall: 46 | return EC_J2KUncompressedBufferTooSmall; 47 | case CompressedBufferTooSmall: 48 | return EC_J2KCompressedBufferTooSmall; 49 | case ImageTypeNotSupported: 50 | return EC_J2KCodecUnsupportedImageType; 51 | case InvalidJlsParameters: 52 | return EC_J2KCodecInvalidParameters; 53 | case ParameterValueNotSupported: 54 | return EC_J2KCodecUnsupportedValue; 55 | case InvalidCompressedData: 56 | return EC_J2KInvalidCompressedData; 57 | case UnsupportedBitDepthForTransform: 58 | return EC_J2KUnsupportedBitDepthForTransform; 59 | case UnsupportedColorTransform: 60 | return EC_J2KUnsupportedColorTransform; 61 | case TooMuchCompressedData: 62 | return EC_J2KTooMuchCompressedData;*/ 63 | default: 64 | return EC_IllegalParameter; 65 | } 66 | } 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /djdecode.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: singleton class that registers decoders for all supported JPEG-2000 processes. 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djdecode.h" 28 | #include "dcmtk/dcmdata/dccodec.h" /* for DcmCodecStruct */ 29 | #include "fmjpeg2k/djcparam.h" 30 | #include "fmjpeg2k/djcodecd.h" 31 | 32 | // initialization of static members 33 | OFBool FMJPEG2KDecoderRegistration::registered_ = OFFalse; 34 | DJPEG2KCodecParameter *FMJPEG2KDecoderRegistration::cp_ = NULL; 35 | DJPEG2KDecoder *FMJPEG2KDecoderRegistration::decoder_ = NULL; 36 | 37 | void FMJPEG2KDecoderRegistration::registerCodecs( 38 | J2K_UIDCreation uidcreation, 39 | J2K_PlanarConfiguration planarconfig, 40 | OFBool ignoreOffsetTable) 41 | { 42 | if (! registered_) 43 | { 44 | cp_ = new DJPEG2KCodecParameter(uidcreation, planarconfig, ignoreOffsetTable); 45 | if (cp_) 46 | { 47 | decoder_ = new DJPEG2KDecoder(); 48 | if (decoder_) DcmCodecList::registerCodec(decoder_, NULL, cp_); 49 | 50 | registered_ = OFTrue; 51 | } 52 | } 53 | } 54 | 55 | void FMJPEG2KDecoderRegistration::cleanup() 56 | { 57 | if (registered_) 58 | { 59 | DcmCodecList::deregisterCodec(decoder_); 60 | delete decoder_; 61 | delete cp_; 62 | registered_ = OFFalse; 63 | #ifdef DEBUG 64 | // not needed but useful for debugging purposes 65 | decoder_ = NULL; 66 | cp_ = NULL; 67 | #endif 68 | } 69 | } 70 | 71 | OFString FMJPEG2KDecoderRegistration::getLibraryVersionString() 72 | { 73 | return FMJPEG2K_JPEG_VERSION_STRING; 74 | } 75 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djdecode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: singleton class that registers the decoder for all supported JPEG-2000 processes. 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJDECODE_H 19 | #define FMJPEG2K_DJDECODE_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/ofstd/oftypes.h" /* for OFBool */ 23 | #include "djlsutil.h" /* for enums */ 24 | 25 | class DJPEG2KCodecParameter; 26 | class DJPEG2KDecoder; 27 | 28 | /** singleton class that registers decoders for all supported JPEG 2000 processes. 29 | */ 30 | class FMJPEG2K_EXPORT FMJPEG2KDecoderRegistration 31 | { 32 | public: 33 | 34 | /** registers decoder for all supported JPEG-LS processes. 35 | * If already registered, call is ignored unless cleanup() has 36 | * been performed before. 37 | * @param uidcreation flag indicating whether or not 38 | * a new SOP Instance UID should be assigned upon decompression. 39 | * @param planarconfig flag indicating how planar configuration 40 | * of color images should be encoded upon decompression. 41 | * @param ignoreOffsetTable flag indicating whether to ignore the offset table when decompressing multiframe images 42 | */ 43 | static void registerCodecs( 44 | J2K_UIDCreation uidcreation = EJ2KUC_default, 45 | J2K_PlanarConfiguration planarconfig = EJ2KPC_restore, 46 | OFBool ignoreOffsetTable = OFFalse); 47 | 48 | /** deregisters decoders. 49 | * Attention: Must not be called while other threads might still use 50 | * the registered codecs, e.g. because they are currently decoding 51 | * DICOM data sets through dcmdata. 52 | */ 53 | static void cleanup(); 54 | 55 | /** get version information of the CharLS library. 56 | * Typical output format: "CharLS, Revision 55020 (modified)" 57 | * @return name and version number of the CharLS library 58 | */ 59 | static OFString getLibraryVersionString(); 60 | 61 | private: 62 | 63 | /// flag indicating whether the decoders are already registered. 64 | static OFBool registered_; 65 | 66 | /// pointer to codec parameter shared by all decoders 67 | static DJPEG2KCodecParameter *cp_; 68 | 69 | /// pointer to decoder 70 | static DJPEG2KDecoder *decoder_; 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /djrparam.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: representation parameter for JPEG-2000 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djrparam.h" 28 | #include "dcmtk/ofstd/ofstd.h" 29 | 30 | FMJPEG2KRepresentationParameter::FMJPEG2KRepresentationParameter( 31 | Uint16 nearlosslessPSNR, 32 | OFBool losslessProcess) 33 | : DcmRepresentationParameter() 34 | , nearlosslessPSNR_(nearlosslessPSNR) 35 | , losslessProcess_(losslessProcess) 36 | { 37 | } 38 | 39 | FMJPEG2KRepresentationParameter::FMJPEG2KRepresentationParameter(const FMJPEG2KRepresentationParameter& arg) 40 | : DcmRepresentationParameter(arg) 41 | , nearlosslessPSNR_(arg.nearlosslessPSNR_) 42 | , losslessProcess_(arg.losslessProcess_) 43 | { 44 | } 45 | 46 | FMJPEG2KRepresentationParameter::~FMJPEG2KRepresentationParameter() 47 | { 48 | } 49 | 50 | DcmRepresentationParameter *FMJPEG2KRepresentationParameter::clone() const 51 | { 52 | return new FMJPEG2KRepresentationParameter(*this); 53 | } 54 | 55 | const char *FMJPEG2KRepresentationParameter::className() const 56 | { 57 | return "FMJP2KRepresentationParameter"; 58 | } 59 | 60 | OFBool FMJPEG2KRepresentationParameter::operator==(const DcmRepresentationParameter &arg) const 61 | { 62 | const char *argname = arg.className(); 63 | if (argname) 64 | { 65 | OFString argstring(argname); 66 | if (argstring == className()) 67 | { 68 | const FMJPEG2KRepresentationParameter& argll = OFreinterpret_cast(const FMJPEG2KRepresentationParameter &, arg); 69 | if (losslessProcess_ && argll.losslessProcess_) return OFTrue; 70 | else if (losslessProcess_ != argll.losslessProcess_) return OFFalse; 71 | else if (nearlosslessPSNR_ != argll.nearlosslessPSNR_) return OFFalse; 72 | return OFTrue; 73 | } 74 | } 75 | return OFFalse; 76 | } 77 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djrparam.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: representation parameters for JPEG 2000 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJRPARAM_H 19 | #define FMJPEG2K_DJRPARAM_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/dcmdata/dcpixel.h" /* for class DcmRepresentationParameter */ 23 | #include "dldefine.h" 24 | 25 | /** representation parameter for JPEG 2000 26 | */ 27 | class FMJPEG2K_EXPORT FMJPEG2KRepresentationParameter: public DcmRepresentationParameter 28 | { 29 | public: 30 | 31 | /** constructor 32 | * @param nearlosslessDeviation used as parameter NEAR in JPEG 2000 nearlossless-encoding process 33 | * @param losslessProcess true if lossless process is requested 34 | */ 35 | FMJPEG2KRepresentationParameter( 36 | Uint16 PSNR = 0, 37 | OFBool losslessProcess = OFTrue); 38 | 39 | /// copy constructor 40 | FMJPEG2KRepresentationParameter(const FMJPEG2KRepresentationParameter& arg); 41 | 42 | /// destructor 43 | virtual ~FMJPEG2KRepresentationParameter(); 44 | 45 | /** this methods creates a copy of type DcmRepresentationParameter * 46 | * it must be overweritten in every subclass. 47 | * @return copy of this object 48 | */ 49 | virtual DcmRepresentationParameter *clone() const; 50 | 51 | /** returns the class name as string. 52 | * can be used in operator== as poor man's RTTI replacement. 53 | */ 54 | virtual const char *className() const; 55 | 56 | /** compares an object to another DcmRepresentationParameter. 57 | * Implementation must make sure that classes are comparable. 58 | * @param arg representation parameter to compare with 59 | * @return true if equal, false otherwise. 60 | */ 61 | virtual OFBool operator==(const DcmRepresentationParameter &arg) const; 62 | 63 | /** returns the desired NEAR parameter 64 | * @return return desired NEAR-Parameter 65 | */ 66 | Uint16 getnearlosslessPSNR() const 67 | { 68 | return nearlosslessPSNR_; 69 | } 70 | 71 | /** returns true if lossless compression is desired 72 | * @return true if lossless compression is desired 73 | */ 74 | OFBool useLosslessProcess() const 75 | { 76 | return losslessProcess_; 77 | } 78 | 79 | private: 80 | 81 | /** desired PSNR parameter 82 | * Default is 0 (automatic selection). 83 | * Ignored during lossless compression. 84 | */ 85 | Uint16 nearlosslessPSNR_; 86 | 87 | /// true if lossless process should be used even in lossy transfer syntax 88 | OFBool losslessProcess_; 89 | 90 | }; 91 | 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /djutils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: Support code for fmjpeg2k 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djlsutil.h" 28 | #include "dcmtk/dcmdata/dcerror.h" 29 | 30 | OFLogger DCM_fmjp2kLogger = OFLog::getLogger("fmjpeg2k"); 31 | 32 | #define MAKE_FMJPEG2K_ERROR(number, name, description) \ 33 | makeOFConditionConst(EC_ ## name, OFM_dcmjpls, number, OF_error, description) 34 | 35 | MAKE_FMJPEG2K_ERROR( 1, J2KUncompressedBufferTooSmall, "Uncompressed pixel data too short for uncompressed image"); 36 | MAKE_FMJPEG2K_ERROR( 2, J2KCompressedBufferTooSmall, "Allocated too small buffer for compressed image data"); 37 | MAKE_FMJPEG2K_ERROR( 3, J2KCodecUnsupportedImageType, "Codec does not support this JPEG-LS image"); 38 | MAKE_FMJPEG2K_ERROR( 4, J2KCodecInvalidParameters, "Codec received invalid compression parameters"); 39 | MAKE_FMJPEG2K_ERROR( 5, J2KCodecUnsupportedValue, "Codec received unsupported compression parameters"); 40 | MAKE_FMJPEG2K_ERROR( 6, J2KInvalidCompressedData, "Invalid compressed image data"); 41 | MAKE_FMJPEG2K_ERROR( 7, J2KUnsupportedBitDepthForTransform, "Codec does not support the image's color transformation with this bit depth"); 42 | MAKE_FMJPEG2K_ERROR( 8, J2KUnsupportedColorTransform, "Codec does not support the image's color transformation"); 43 | MAKE_FMJPEG2K_ERROR( 9, J2KUnsupportedBitDepth, "Unsupported bit depth in JPEG-LS transfer syntax"); 44 | MAKE_FMJPEG2K_ERROR(10, J2KCannotComputeNumberOfFragments, "Cannot compute number of fragments for JPEG-LS frame"); 45 | MAKE_FMJPEG2K_ERROR(11, J2KImageDataMismatch, "Image data mismatch between DICOM header and JPEG-LS bitstream"); 46 | MAKE_FMJPEG2K_ERROR(12, J2KUnsupportedPhotometricInterpretation, "Unsupported photometric interpretation for near-lossless JPEG-LS compression"); 47 | MAKE_FMJPEG2K_ERROR(13, J2KUnsupportedPixelRepresentation, "Unsupported pixel representation for near-lossless JPEG-LS compression"); 48 | MAKE_FMJPEG2K_ERROR(14, J2KUnsupportedImageType, "Unsupported type of image for JPEG-LS compression"); 49 | MAKE_FMJPEG2K_ERROR(15, J2KTooMuchCompressedData, "Too much compressed data, trailing data after image"); 50 | -------------------------------------------------------------------------------- /djencode.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: singleton class that registers encoders for all supported JPEG-2000 processes. 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djencode.h" 28 | #include "dcmtk/dcmdata/dccodec.h" /* for DcmCodecStruct */ 29 | #include "fmjpeg2k/djcparam.h" 30 | #include "fmjpeg2k/djcodece.h" 31 | 32 | // initialization of static members 33 | OFBool FMJPEG2KEncoderRegistration::registered_ = OFFalse; 34 | DJPEG2KCodecParameter *FMJPEG2KEncoderRegistration::cp_ = NULL; 35 | DJPEG2KLosslessEncoder *FMJPEG2KEncoderRegistration::losslessencoder_ = NULL; 36 | DJPEG2KNearLosslessEncoder *FMJPEG2KEncoderRegistration::nearlosslessencoder_ = NULL; 37 | 38 | 39 | void FMJPEG2KEncoderRegistration::registerCodecs( 40 | OFBool jp2k_optionsEnabled, 41 | Uint16 jp2k_cblkwidth, 42 | Uint16 jp2k_cblkheight, 43 | OFBool preferCookedEncoding, 44 | Uint32 fragmentSize, 45 | OFBool createOffsetTable, 46 | J2K_UIDCreation uidCreation, 47 | OFBool convertToSC) 48 | { 49 | if (! registered_) 50 | { 51 | cp_ = new DJPEG2KCodecParameter(jp2k_optionsEnabled, jp2k_cblkwidth, jp2k_cblkheight, 52 | preferCookedEncoding, fragmentSize, createOffsetTable, uidCreation, 53 | convertToSC, EJ2KPC_restore, OFFalse); 54 | 55 | if (cp_) 56 | { 57 | losslessencoder_ = new DJPEG2KLosslessEncoder(); 58 | if (losslessencoder_) DcmCodecList::registerCodec(losslessencoder_, NULL, cp_); 59 | nearlosslessencoder_ = new DJPEG2KNearLosslessEncoder(); 60 | if (nearlosslessencoder_) DcmCodecList::registerCodec(nearlosslessencoder_, NULL, cp_); 61 | registered_ = OFTrue; 62 | } 63 | } 64 | } 65 | 66 | void FMJPEG2KEncoderRegistration::cleanup() 67 | { 68 | if (registered_) 69 | { 70 | DcmCodecList::deregisterCodec(losslessencoder_); 71 | DcmCodecList::deregisterCodec(nearlosslessencoder_); 72 | delete losslessencoder_; 73 | delete nearlosslessencoder_; 74 | delete cp_; 75 | registered_ = OFFalse; 76 | #ifdef DEBUG 77 | // not needed but useful for debugging purposes 78 | losslessencoder_ = NULL; 79 | nearlosslessencoder_ = NULL; 80 | cp_ = NULL; 81 | #endif 82 | } 83 | } 84 | 85 | OFString FMJPEG2KEncoderRegistration::getLibraryVersionString() 86 | { 87 | return FMJPEG2K_JPEG_VERSION_STRING; 88 | } 89 | -------------------------------------------------------------------------------- /djcparam.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: codec parameter class for JPEG-LS codecs 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djcparam.h" 28 | #include "dcmtk/ofstd/ofstd.h" 29 | 30 | DJPEG2KCodecParameter::DJPEG2KCodecParameter( 31 | OFBool jp2k_optionsEnabled, 32 | Uint16 jp2k_cblkwidth, 33 | Uint16 jp2k_cblkheight, 34 | OFBool preferCookedEncoding, 35 | Uint32 fragmentSize, 36 | OFBool createOffsetTable, 37 | J2K_UIDCreation uidCreation, 38 | OFBool convertToSC, 39 | J2K_PlanarConfiguration planarConfiguration, 40 | OFBool ignoreOffsetTble) 41 | : DcmCodecParameter() 42 | , jp2k_optionsEnabled_(jp2k_optionsEnabled) 43 | , jp2k_cblkwidth_(jp2k_cblkwidth) 44 | , jp2k_cblkheight_(jp2k_cblkheight) 45 | , fragmentSize_(fragmentSize) 46 | , createOffsetTable_(createOffsetTable) 47 | , preferCookedEncoding_(preferCookedEncoding) 48 | , uidCreation_(uidCreation) 49 | , convertToSC_(convertToSC) 50 | , planarConfiguration_(planarConfiguration) 51 | , ignoreOffsetTable_(ignoreOffsetTble) 52 | { 53 | } 54 | 55 | 56 | DJPEG2KCodecParameter::DJPEG2KCodecParameter( 57 | J2K_UIDCreation uidCreation, 58 | J2K_PlanarConfiguration planarConfiguration, 59 | OFBool ignoreOffsetTble) 60 | : DcmCodecParameter() 61 | , jp2k_optionsEnabled_(OFFalse) 62 | , jp2k_cblkwidth_(0) 63 | , jp2k_cblkheight_(1) 64 | , fragmentSize_(0) 65 | , createOffsetTable_(OFTrue) 66 | , preferCookedEncoding_(OFTrue) 67 | , uidCreation_(uidCreation) 68 | , convertToSC_(OFFalse) 69 | , planarConfiguration_(planarConfiguration) 70 | , ignoreOffsetTable_(ignoreOffsetTble) 71 | { 72 | } 73 | 74 | DJPEG2KCodecParameter::DJPEG2KCodecParameter(const DJPEG2KCodecParameter& arg) 75 | : DcmCodecParameter(arg) 76 | 77 | , jp2k_optionsEnabled_(arg.jp2k_optionsEnabled_) 78 | , jp2k_cblkwidth_(arg.jp2k_cblkwidth_) 79 | , jp2k_cblkheight_(arg.jp2k_cblkheight_) 80 | , fragmentSize_(arg.fragmentSize_) 81 | , createOffsetTable_(arg.createOffsetTable_) 82 | , preferCookedEncoding_(arg.preferCookedEncoding_) 83 | , uidCreation_(arg.uidCreation_) 84 | , convertToSC_(arg.convertToSC_) 85 | , planarConfiguration_(arg.planarConfiguration_) 86 | , ignoreOffsetTable_(arg.ignoreOffsetTable_) 87 | { 88 | } 89 | 90 | DJPEG2KCodecParameter::~DJPEG2KCodecParameter() 91 | { 92 | } 93 | 94 | DcmCodecParameter *DJPEG2KCodecParameter::clone() const 95 | { 96 | return new DJPEG2KCodecParameter(*this); 97 | } 98 | 99 | const char *DJPEG2KCodecParameter::className() const 100 | { 101 | return "DJPEG2KCodecParameter"; 102 | } 103 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djencode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: singleton class that registers encoders for all supported JPEG 2000 processes. 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJENCODE_H 19 | #define FMJPEG2K_DJENCODE_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/ofstd/oftypes.h" /* for OFBool */ 23 | #include "djlsutil.h" 24 | #include "dcmtk/dcmdata/dctypes.h" /* for Uint32 */ 25 | #include "djcparam.h" /* for class DJP2KCodecParameter */ 26 | 27 | class DJPEG2KCodecParameter; 28 | class DJPEG2KLosslessEncoder; 29 | class DJPEG2KNearLosslessEncoder; 30 | 31 | /** singleton class that registers encoders for all supported JPEG 2000 processes. 32 | */ 33 | class FMJPEG2K_EXPORT FMJPEG2KEncoderRegistration 34 | { 35 | public: 36 | 37 | /** registers encoders for all supported JPEG 2000 processes. 38 | * If already registered, call is ignored unless cleanup() has 39 | * been performed before. 40 | * @param jp2k_optionsEnabled enable/disable use of all five JPEG 2000 parameters 41 | * @param jp2k_cblkwidth JPEG-2000 parameter "Threshold 1" (used for quantization) 42 | * @param jp2k_cblkheight JPEG-2000 parameter "Threshold 2" 43 | * @param preferCookedEncoding true if the "cooked" lossless encoder should be preferred over the "raw" one 44 | * @param fragmentSize maximum fragment size (in kbytes) for compression, 0 for unlimited. 45 | * @param createOffsetTable create offset table during image compression 46 | * @param uidCreation mode for SOP Instance UID creation 47 | * @param convertToSC flag indicating whether image should be converted to Secondary Capture upon compression 48 | * @param jplsInterleaveMode flag describing which interleave the JPEG-LS datastream should use 49 | */ 50 | static void registerCodecs( 51 | OFBool jp2k_optionsEnabled = OFFalse, 52 | Uint16 jp2k_cblkwidth = 64, 53 | Uint16 jp2k_cblkheight = 64, 54 | OFBool preferCookedEncoding = OFTrue, 55 | Uint32 fragmentSize = 0, 56 | OFBool createOffsetTable = OFTrue, 57 | J2K_UIDCreation uidCreation = EJ2KUC_default, 58 | OFBool convertToSC = OFFalse); 59 | 60 | /** deregisters encoders. 61 | * Attention: Must not be called while other threads might still use 62 | * the registered codecs, e.g. because they are currently encoding 63 | * DICOM data sets through dcmdata. 64 | */ 65 | static void cleanup(); 66 | 67 | /** get version information of the CharLS library. 68 | * Typical output format: "CharLS, Revision 55020 (modified)" 69 | * @return name and version number of the CharLS library 70 | */ 71 | static OFString getLibraryVersionString(); 72 | 73 | private: 74 | 75 | /// flag indicating whether the encoders are already registered. 76 | static OFBool registered_; 77 | 78 | /// pointer to codec parameter shared by all encoders 79 | static DJPEG2KCodecParameter *cp_; 80 | 81 | /// pointer to encoder for lossless JPEG 2000 82 | static DJPEG2KLosslessEncoder *losslessencoder_; 83 | 84 | /// pointer to encoder for lossy JPEG 2000 85 | static DJPEG2KNearLosslessEncoder *nearlosslessencoder_; 86 | 87 | }; 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(fmjpeg2k) 4 | 5 | set (fmjpeg2k_VERSION_MAJOR 1) 6 | set (fmjpeg2k_VERSION_MINOR 0) 7 | set (fmjpeg2k_VERSION_BUILD 3) 8 | set(fmjpeg2k_VERSION "${fmjpeg2k_VERSION_MAJOR}.${fmjpeg2k_VERSION_MINOR}.${fmjpeg2k_VERSION_BUILD}") 9 | 10 | option(BUILD_SHARED_LIBS "Build fmjpeg2k shared library and link executables against it." ON) 11 | 12 | set (CMAKE_CXX_STANDARD 11) 13 | 14 | if(NOT BUILD_SHARED_LIBS) 15 | add_definitions(-DOPJ_STATIC) 16 | endif() 17 | 18 | find_package(DCMTK) 19 | find_package(OpenJPEG) 20 | 21 | INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${DCMTK_INCLUDE_DIRS} ${OPENJPEG_INCLUDE_DIRS}) 22 | LINK_DIRECTORIES(${FMJPEG2K}/lib) 23 | 24 | add_definitions(-Dfmjpeg2k_EXPORTS) 25 | 26 | set(fmjpeg2k_HEADERS 27 | include/fmjpeg2k/djcodecd.h 28 | include/fmjpeg2k/djcodece.h 29 | include/fmjpeg2k/djcparam.h 30 | include/fmjpeg2k/djdecode.h 31 | include/fmjpeg2k/djencode.h 32 | include/fmjpeg2k/djerror.h 33 | include/fmjpeg2k/djlsutil.h 34 | include/fmjpeg2k/djrparam.h 35 | include/fmjpeg2k/dldefine.h 36 | include/fmjpeg2k/memory_file.h 37 | ) 38 | 39 | set(fmjpeg2k_SRCS 40 | ${fmjpeg2k_HEADERS} 41 | djcodecd.cc 42 | djcodece.cc 43 | djcparam.cc 44 | djdecode.cc 45 | djencode.cc 46 | djrparam.cc 47 | djutils.cc 48 | memory_file.cpp 49 | ) 50 | 51 | if(WIN32) 52 | add_definitions(-D_BIND_TO_CURRENT_VCLIBS_VERSION=1) 53 | endif() 54 | 55 | ADD_EXECUTABLE(dcmcjp2k dcmcjp2k.cc) 56 | TARGET_LINK_LIBRARIES(dcmcjp2k 57 | ${DCMTK_LIBRARIES} 58 | ${OPENJPEG_LIBRARIES} 59 | fmjpeg2k 60 | ) 61 | 62 | ADD_EXECUTABLE(dcmdjp2k dcmdjp2k.cc) 63 | TARGET_LINK_LIBRARIES(dcmdjp2k 64 | ${DCMTK_LIBRARIES} 65 | ${OPENJPEG_LIBRARIES} 66 | fmjpeg2k 67 | ) 68 | 69 | 70 | add_library(fmjpeg2k ${fmjpeg2k_SRCS}) 71 | set(fmjpeg2k_LIBRARY_NAME fmjpeg2k) 72 | TARGET_LINK_LIBRARIES(fmjpeg2k 73 | ${DCMTK_LIBRARIES} 74 | ${OPENJPEG_LIBRARIES} 75 | ) 76 | 77 | include(GenerateExportHeader) 78 | generate_export_header(fmjpeg2k) 79 | set_property(TARGET fmjpeg2k PROPERTY VERSION ${fmjpeg2k_VERSION}) 80 | set_property(TARGET fmjpeg2k PROPERTY SOVERSION 1) 81 | set_property(TARGET fmjpeg2k PROPERTY 82 | INTERFACE_fmjpeg2k_MAJOR_VERSION 1) 83 | set_property(TARGET fmjpeg2k APPEND PROPERTY 84 | COMPATIBLE_INTERFACE_STRING fmjpeg2k_MAJOR_VERSION 85 | ) 86 | 87 | install(TARGETS dcmdjp2k EXPORT fmjpeg2kTargets 88 | RUNTIME DESTINATION bin 89 | ) 90 | 91 | install(TARGETS dcmcjp2k EXPORT fmjpeg2kTargets 92 | RUNTIME DESTINATION bin 93 | ) 94 | 95 | install(TARGETS fmjpeg2k EXPORT fmjpeg2kTargets 96 | LIBRARY DESTINATION lib 97 | ARCHIVE DESTINATION lib 98 | RUNTIME DESTINATION bin 99 | ) 100 | 101 | install( 102 | FILES 103 | ${fmjpeg2k_HEADERS} 104 | DESTINATION 105 | include/fmjpeg2k 106 | COMPONENT 107 | Devel 108 | ) 109 | 110 | include(CMakePackageConfigHelpers) 111 | write_basic_package_version_file( 112 | "${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2k/fmjpeg2kConfigVersion.cmake" 113 | VERSION ${fmjpeg2k_VERSION} 114 | COMPATIBILITY AnyNewerVersion 115 | ) 116 | 117 | export(TARGETS fmjpeg2k 118 | FILE "${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2kExports.cmake" 119 | ) 120 | configure_file( ${CMAKE_SOURCE_DIR}/cmake/fmjpeg2kConfig.cmake.in 121 | ${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2kConfig.cmake 122 | @ONLY 123 | ) 124 | #configure_file(cmake/fmjpeg2kConfig.cmake 125 | # "${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2k/fmjpeg2kConfig.cmake" 126 | # COPYONLY 127 | #) 128 | 129 | set(ConfigPackageLocation lib/cmake/fmjpeg2k) 130 | install(EXPORT fmjpeg2kTargets 131 | FILE 132 | fmjpeg2kTargets.cmake 133 | DESTINATION 134 | ${ConfigPackageLocation} 135 | ) 136 | 137 | 138 | install( 139 | FILES 140 | "${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2kConfig.cmake" 141 | "${CMAKE_CURRENT_BINARY_DIR}/fmjpeg2k/fmjpeg2kConfigVersion.cmake" 142 | DESTINATION 143 | ${ConfigPackageLocation} 144 | COMPONENT 145 | Devel 146 | ) 147 | -------------------------------------------------------------------------------- /memory_file.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "fmjpeg2k/memory_file.h" 23 | 24 | OPJ_SIZE_T opj_read_from_memory(void * p_buffer, OPJ_SIZE_T p_nb_bytes, DecodeData* p_file); 25 | OPJ_SIZE_T opj_write_to_memory (void * p_buffer, OPJ_SIZE_T p_nb_bytes, DecodeData* p_file); 26 | OPJ_OFF_T opj_skip_from_memory (OPJ_OFF_T p_nb_bytes, DecodeData * p_file); 27 | OPJ_BOOL opj_seek_from_memory (OPJ_OFF_T p_nb_bytes, DecodeData * p_file); 28 | 29 | OPJ_SIZE_T opj_read_from_memory(void * p_buffer, OPJ_SIZE_T nb_bytes, DecodeData* p_user_data) 30 | { 31 | if (!p_user_data || !p_user_data->src_data || p_user_data->src_size == 0) { 32 | return -1; 33 | } 34 | // Reads at EOF return an error code. 35 | if (p_user_data->offset >= p_user_data->src_size) { 36 | return -1; 37 | } 38 | OPJ_SIZE_T bufferLength = p_user_data->src_size - p_user_data->offset; 39 | OPJ_SIZE_T readlength = nb_bytes < bufferLength ? nb_bytes : bufferLength; 40 | memcpy(p_buffer, &p_user_data->src_data[p_user_data->offset], readlength); 41 | p_user_data->offset += readlength; 42 | return readlength; 43 | } 44 | 45 | OPJ_SIZE_T opj_write_to_memory (void * p_buffer, OPJ_SIZE_T nb_bytes, DecodeData* p_user_data) 46 | { 47 | if (!p_user_data || !p_user_data->src_data || p_user_data->src_size == 0) { 48 | return -1; 49 | } 50 | // Writes at EOF return an error code. 51 | if (p_user_data->offset >= p_user_data->src_size) { 52 | return -1; 53 | } 54 | OPJ_SIZE_T bufferLength = p_user_data->src_size - p_user_data->offset; 55 | OPJ_SIZE_T writeLength = nb_bytes < bufferLength ? nb_bytes : bufferLength; 56 | memcpy(&p_user_data->src_data[p_user_data->offset], p_buffer, writeLength); 57 | p_user_data->offset += writeLength; 58 | return writeLength; 59 | } 60 | 61 | OPJ_OFF_T opj_skip_from_memory (OPJ_OFF_T nb_bytes, DecodeData * p_user_data) 62 | { 63 | if (!p_user_data || !p_user_data->src_data || p_user_data->src_size == 0) { 64 | return -1; 65 | } 66 | // Offsets are signed and may indicate a negative skip. Do not support this 67 | // because of the strange return convention where either bytes skipped or 68 | // -1 is returned. Following that convention, a successful relative seek of 69 | // -1 bytes would be required to to give the same result as the error case. 70 | if (nb_bytes < 0) { 71 | return -1; 72 | } 73 | 74 | // check overflow 75 | if (/* (nb_bytes > 0) && */(p_user_data->offset > std::numeric_limits::max() - nb_bytes)) 76 | return -1; 77 | 78 | OPJ_SIZE_T newoffset = p_user_data->offset + nb_bytes; 79 | 80 | // skipped past eof? 81 | if(newoffset > p_user_data->src_size) { 82 | // number of actual skipped bytes 83 | nb_bytes = p_user_data->src_size - p_user_data->offset; 84 | p_user_data->offset = p_user_data->src_size; 85 | } 86 | else { 87 | p_user_data->offset = newoffset; 88 | } 89 | return nb_bytes; 90 | } 91 | 92 | OPJ_BOOL opj_seek_from_memory (OPJ_OFF_T nb_bytes, DecodeData * p_user_data) 93 | { 94 | if (!p_user_data || !p_user_data->src_data || p_user_data->src_size == 0) { 95 | return OPJ_FALSE; 96 | } 97 | 98 | // backwards? 99 | if(nb_bytes < 0) 100 | return OPJ_FALSE; 101 | 102 | p_user_data->offset = std::min((OPJ_SIZE_T) nb_bytes, p_user_data->src_size); 103 | return OPJ_TRUE; 104 | } 105 | 106 | opj_stream_t* OPJ_CALLCONV opj_stream_create_memory_stream(DecodeData* p_mem,OPJ_UINT32 p_size, bool p_is_read_stream) 107 | { 108 | opj_stream_t* l_stream = NULL; 109 | if (! p_mem) 110 | return NULL; 111 | 112 | l_stream = opj_stream_create(p_size,p_is_read_stream); 113 | if (!l_stream) 114 | return NULL; 115 | 116 | opj_stream_set_user_data(l_stream, p_mem, NULL); 117 | opj_stream_set_user_data_length(l_stream, p_mem->src_size); 118 | opj_stream_set_read_function(l_stream,(opj_stream_read_fn) opj_read_from_memory); 119 | opj_stream_set_write_function(l_stream, (opj_stream_write_fn) opj_write_to_memory); 120 | opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn) opj_skip_from_memory); 121 | opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn) opj_seek_from_memory); 122 | return l_stream; 123 | } 124 | 125 | void msg_callback(const char* msg, void* f) 126 | { 127 | } -------------------------------------------------------------------------------- /include/fmjpeg2k/djlsutil.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: enumerations, error constants and helper functions for dcmjpls 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJLSUTILS_H 19 | #define FMJPEG2K_DJLSUTILS_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/ofstd/ofcond.h" /* for class OFCondition */ 23 | #include "dcmtk/oflog/oflog.h" 24 | #include "dldefine.h" 25 | 26 | 27 | #define FMJPEG2K_JPEG_VERSION_STRING "fmjpeg2koj - OpenJPEG (unmodified)" 28 | 29 | 30 | // global definitions for logging mechanism provided by the oflog module 31 | 32 | extern FMJPEG2K_EXPORT OFLogger DCM_fmjp2kLogger; 33 | 34 | #define FMJPEG2K_TRACE(msg) OFLOG_TRACE(DCM_fmjp2kLogger, msg) 35 | #define FMJPEG2K_DEBUG(msg) OFLOG_DEBUG(DCM_fmjp2kLogger, msg) 36 | #define FMJPEG2K_INFO(msg) OFLOG_INFO(DCM_fmjp2kLogger, msg) 37 | #define FMJPEG2K_WARN(msg) OFLOG_WARN(DCM_fmjp2kLogger, msg) 38 | #define FMJPEG2K_ERROR(msg) OFLOG_ERROR(DCM_fmjp2kLogger, msg) 39 | #define FMJPEG2K_FATAL(msg) OFLOG_FATAL(DCM_fmjp2kLogger, msg) 40 | 41 | 42 | // include this file in doxygen documentation 43 | 44 | /** @file djlsutil.h 45 | * @brief enumerations, error constants and helper functions for the dcmjpls module 46 | */ 47 | 48 | 49 | /** describes the condition under which a compressed or decompressed image 50 | * receives a new SOP instance UID. 51 | */ 52 | enum J2K_UIDCreation 53 | { 54 | /** Upon compression, assign new SOP instance UID if compression is lossy. 55 | * Upon decompression never assign new SOP instance UID. 56 | */ 57 | EJ2KUC_default, 58 | 59 | /// always assign new SOP instance UID on compression and decompression 60 | EJ2KUC_always, 61 | 62 | /// never assign new SOP instance UID 63 | EJ2KUC_never 64 | }; 65 | 66 | /** describes how the decoder should handle planar configuration of 67 | * decompressed color images. 68 | */ 69 | enum J2K_PlanarConfiguration 70 | { 71 | /// restore planar configuration as indicated in data set 72 | EJ2KPC_restore, 73 | 74 | /** automatically determine whether color-by-plane is required from 75 | * the SOP Class UID and decompressed photometric interpretation 76 | */ 77 | EJ2KPC_auto, 78 | 79 | /// always create color-by-pixel planar configuration 80 | EJ2KPC_colorByPixel, 81 | 82 | /// always create color-by-plane planar configuration 83 | EJ2KPC_colorByPlane 84 | }; 85 | 86 | /** describes how the encoder handles the image bit depth 87 | * upon lossy compression. 88 | */ 89 | enum J2K_CompressionBitDepth 90 | { 91 | /// keep original bit depth 92 | EJ2KBD_original, 93 | 94 | /** limit bit depth to a certain value, i.e. scale down 95 | * if the original image bit depth is larger 96 | */ 97 | EJ2KBD_limit, 98 | 99 | /** force bit depth to a certain value, i.e. scale up 100 | * or scale down the original image to match the given 101 | * bit depth. 102 | */ 103 | EJ2KBD_force 104 | }; 105 | 106 | 107 | // CONDITION CONSTANTS 108 | 109 | /// error condition constant: Too small buffer used for image data (internal error) 110 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUncompressedBufferTooSmall; 111 | 112 | /// error condition constant: Too small buffer used for compressed image data (internal error) 113 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KCompressedBufferTooSmall; 114 | 115 | /// error condition constant: The image uses some features which the codec does not support 116 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KCodecUnsupportedImageType; 117 | 118 | /// error condition constant: The codec was fed with invalid parameters (e.g. height = -1) 119 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KCodecInvalidParameters; 120 | 121 | /// error condition constant: The codec was fed with unsupported parameters (e.g. 32 bit per sample) 122 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KCodecUnsupportedValue; 123 | 124 | /// error condition constant: The compressed image is invalid 125 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KInvalidCompressedData; 126 | 127 | /// error condition constant: The images' color transformation is not supported in this bit depth 128 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedBitDepthForTransform; 129 | 130 | /// error condition constant: The images' color transformation is not supported 131 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedColorTransform; 132 | 133 | /// error condition constant: Unsupported bit depth in JPEG-LS transfer syntax 134 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedBitDepth; 135 | 136 | /// error condition constant: Cannot compute number of fragments for JPEG-LS frame 137 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KCannotComputeNumberOfFragments; 138 | 139 | /// error condition constant: Image data mismatch between DICOM header and JPEG-LS bitstream 140 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KImageDataMismatch; 141 | 142 | /// error condition constant: Unsupported photometric interpretation for near-lossless JPEG-LS compression 143 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedPhotometricInterpretation; 144 | 145 | /// error condition constant: Unsupported pixel representation for near-lossless JPEG-LS compression 146 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedPixelRepresentation; 147 | 148 | /// error condition constant: Unsupported type of image for JPEG-LS compression 149 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KUnsupportedImageType; 150 | 151 | /// error condition constant: Trailing data after image 152 | extern FMJPEG2K_EXPORT const OFConditionConst EC_J2KTooMuchCompressedData; 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djcparam.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: codec parameter class JPEG-2000 codecs 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJCPARAM_H 19 | #define FMJPEG2K_DJCPARAM_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/dcmdata/dccodec.h" /* for DcmCodecParameter */ 23 | #include "djlsutil.h" /* for enums */ 24 | 25 | /** codec parameter for JPEG-2000 codecs 26 | */ 27 | class FMJPEG2K_EXPORT DJPEG2KCodecParameter: public DcmCodecParameter 28 | { 29 | public: 30 | 31 | 32 | /** constructor, for use with encoders. 33 | * @param jp2k_optionsEnabled enable/disable use of all JPEG-2000 parameters 34 | * @param jp2k_cblkwidth JPEG-2000 parameter "cblkwidth" 35 | * @param jp2k_cblkheight JPEG-2000 parameter "cblkheight" 36 | * @param preferCookedEncoding true if the "cooked" lossless encoder should be preferred over the "raw" one 37 | * @param fragmentSize maximum fragment size (in kbytes) for compression, 0 for unlimited. 38 | * @param createOffsetTable create offset table during image compression 39 | * @param uidCreation mode for SOP Instance UID creation 40 | * @param convertToSC flag indicating whether image should be converted to Secondary Capture upon compression 41 | * @param planarConfiguration flag describing how planar configuration of decompressed color images should be handled 42 | * @param ignoreOffsetTable flag indicating whether to ignore the offset table when decompressing multiframe images 43 | */ 44 | DJPEG2KCodecParameter( 45 | OFBool jp2k_optionsEnabled, 46 | Uint16 jp2k_cblkwidth = 64, 47 | Uint16 jp2k_cblkheight = 64, 48 | OFBool preferCookedEncoding = OFTrue, 49 | Uint32 fragmentSize = 0, 50 | OFBool createOffsetTable = OFTrue, 51 | J2K_UIDCreation uidCreation = EJ2KUC_default, 52 | OFBool convertToSC = OFFalse, 53 | J2K_PlanarConfiguration planarConfiguration = EJ2KPC_restore, 54 | OFBool ignoreOffsetTable = OFFalse); 55 | 56 | /** constructor, for use with decoders. Initializes all encoder options to defaults. 57 | * @param uidCreation mode for SOP Instance UID creation (used both for encoding and decoding) 58 | * @param planarConfiguration flag describing how planar configuration of decompressed color images should be handled 59 | * @param ignoreOffsetTable flag indicating whether to ignore the offset table when decompressing multiframe images 60 | */ 61 | DJPEG2KCodecParameter( 62 | J2K_UIDCreation uidCreation = EJ2KUC_default, 63 | J2K_PlanarConfiguration planarConfiguration = EJ2KPC_restore, 64 | OFBool ignoreOffsetTable = OFFalse); 65 | 66 | /// copy constructor 67 | DJPEG2KCodecParameter(const DJPEG2KCodecParameter& arg); 68 | 69 | /// destructor 70 | virtual ~DJPEG2KCodecParameter(); 71 | 72 | /** this methods creates a copy of type DcmCodecParameter * 73 | * it must be overweritten in every subclass. 74 | * @return copy of this object 75 | */ 76 | virtual DcmCodecParameter *clone() const; 77 | 78 | /** returns the class name as string. 79 | * can be used as poor man's RTTI replacement. 80 | */ 81 | virtual const char *className() const; 82 | 83 | /** returns secondary capture conversion flag 84 | * @return secondary capture conversion flag 85 | */ 86 | OFBool getConvertToSC() const 87 | { 88 | return convertToSC_; 89 | } 90 | 91 | /** returns create offset table flag 92 | * @return create offset table flag 93 | */ 94 | OFBool getCreateOffsetTable() const 95 | { 96 | return createOffsetTable_; 97 | } 98 | 99 | /** returns mode for SOP Instance UID creation 100 | * @return mode for SOP Instance UID creation 101 | */ 102 | J2K_UIDCreation getUIDCreation() const 103 | { 104 | return uidCreation_; 105 | } 106 | 107 | /** returns mode for handling planar configuration 108 | * @return mode for handling planar configuration 109 | */ 110 | J2K_PlanarConfiguration getPlanarConfiguration() const 111 | { 112 | return planarConfiguration_; 113 | } 114 | 115 | /** returns flag indicating whether or not the "cooked" lossless encoder 116 | * should be preferred over the "raw" one 117 | * @return raw/cooked lossless encoding flag 118 | */ 119 | OFBool cookedEncodingPreferred() const 120 | { 121 | return preferCookedEncoding_; 122 | } 123 | 124 | /** returns maximum fragment size (in kbytes) for compression, 0 for unlimited. 125 | * @return maximum fragment size for compression 126 | */ 127 | Uint32 getFragmentSize() const 128 | { 129 | return fragmentSize_; 130 | } 131 | 132 | /** returns JPEG-2000 parameter cblkwidth 133 | * @return JPEG-2000 parameter cblkwidth 134 | */ 135 | Uint16 get_cblkwidth() const 136 | { 137 | return jp2k_cblkwidth_; 138 | } 139 | 140 | /** returns JPEG-2000 parameter cblkheight 141 | * @return JPEG-2000 parameter cblkheight 142 | */ 143 | Uint16 get_cblkheight() const 144 | { 145 | return jp2k_cblkheight_; 146 | } 147 | 148 | /** returns true if JPEG-2000 parameters are enabled, false otherwise 149 | * @return true if JPEG-2000 parameters are enabled, false otherwise 150 | */ 151 | OFBool getUseCustomOptions() const 152 | { 153 | return jp2k_optionsEnabled_; 154 | } 155 | 156 | /** returns true if the offset table should be ignored when decompressing multiframe images 157 | * @return true if the offset table should be ignored when decompressing multiframe images 158 | */ 159 | OFBool ignoreOffsetTable() const 160 | { 161 | return ignoreOffsetTable_; 162 | } 163 | 164 | private: 165 | 166 | /// private undefined copy assignment operator 167 | DJPEG2KCodecParameter& operator=(const DJPEG2KCodecParameter&); 168 | 169 | // **************************************************** 170 | // **** Parameters describing the encoding process **** 171 | 172 | /// enable/disable use of JPEG-2000 parameters 173 | OFBool jp2k_optionsEnabled_; 174 | 175 | /// JPEG-2000 parameter "cblockw_init" 176 | Uint16 jp2k_cblkwidth_; 177 | 178 | /// JPEG-2000 parameter "cblockh_init" 179 | Uint16 jp2k_cblkheight_; 180 | 181 | /// maximum fragment size (in kbytes) for compression, 0 for unlimited. 182 | Uint32 fragmentSize_; 183 | 184 | /// create offset table during image compression 185 | OFBool createOffsetTable_; 186 | 187 | /// Flag indicating if the "cooked" lossless encoder should be preferred over the "raw" one 188 | OFBool preferCookedEncoding_; 189 | 190 | /// mode for SOP Instance UID creation (used both for encoding and decoding) 191 | J2K_UIDCreation uidCreation_; 192 | 193 | /// flag indicating whether image should be converted to Secondary Capture upon compression 194 | OFBool convertToSC_; 195 | 196 | // **************************************************** 197 | // **** Parameters describing the decoding process **** 198 | 199 | /// flag describing how planar configuration of decompressed color images should be handled 200 | J2K_PlanarConfiguration planarConfiguration_; 201 | 202 | /// flag indicating if temporary files should be kept, false if they should be deleted after use 203 | OFBool ignoreOffsetTable_; 204 | 205 | }; 206 | 207 | 208 | #endif 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dcmdjp2k.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2007-2014, OFFIS e.V. 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * OFFIS e.V. 9 | * R&D Division Health 10 | * Escherweg 2 11 | * D-26121 Oldenburg, Germany 12 | * 13 | * 14 | * Module: dcmdjpls 15 | * 16 | * Author: Martin Willkomm 17 | * 18 | * Purpose: Decompress DICOM file with JPEG-LS transfer syntax 19 | * 20 | */ 21 | 22 | #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "dcmtk/ofstd/ofstdinc.h" 28 | 29 | #ifdef HAVE_GUSI_H 30 | #include 31 | #endif 32 | 33 | #include "dcmtk/dcmdata/dctk.h" 34 | #include "dcmtk/dcmdata/cmdlnarg.h" 35 | #include "dcmtk/ofstd/ofconapp.h" 36 | #include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ 37 | #include "dcmtk/dcmimage/diregist.h" /* include to support color images */ 38 | #include "fmjpeg2k/djlsutil.h" /* for dcmjpgls typedefs */ 39 | #include "fmjpeg2k/djdecode.h" /* for JPEG-LS decoder */ 40 | 41 | #ifdef USE_LICENSE_FILE 42 | #include "oflice.h" 43 | #endif 44 | 45 | #ifndef OFFIS_CONSOLE_APPLICATION 46 | #define OFFIS_CONSOLE_APPLICATION "dcmdjp2k" 47 | #endif 48 | 49 | static OFLogger dcmdjplsLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); 50 | 51 | static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" 52 | OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; 53 | 54 | // ******************************************** 55 | 56 | 57 | #define SHORTCOL 3 58 | #define LONGCOL 21 59 | 60 | int main(int argc, char *argv[]) 61 | { 62 | 63 | #ifdef HAVE_GUSI_H 64 | GUSISetup(GUSIwithSIOUXSockets); 65 | GUSISetup(GUSIwithInternetSockets); 66 | #endif 67 | 68 | const char *opt_ifname = NULL; 69 | const char *opt_ofname = NULL; 70 | 71 | E_TransferSyntax opt_oxfer = EXS_LittleEndianExplicit; 72 | E_GrpLenEncoding opt_oglenc = EGL_recalcGL; 73 | E_EncodingType opt_oenctype = EET_ExplicitLength; 74 | E_PaddingEncoding opt_opadenc = EPD_noChange; 75 | OFCmdUnsignedInt opt_filepad = 0; 76 | OFCmdUnsignedInt opt_itempad = 0; 77 | E_FileReadMode opt_readMode = ERM_autoDetect; 78 | E_FileWriteMode opt_writeMode = EWM_fileformat; 79 | E_TransferSyntax opt_ixfer = EXS_Unknown; 80 | 81 | // parameter 82 | J2K_UIDCreation opt_uidcreation = EJ2KUC_default; 83 | J2K_PlanarConfiguration opt_planarconfig = EJ2KPC_restore; 84 | OFBool opt_ignoreOffsetTable = OFFalse; 85 | 86 | #ifdef USE_LICENSE_FILE 87 | LICENSE_FILE_DECLARATIONS 88 | #endif 89 | 90 | OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Decode JPEG-LS compressed DICOM file", rcsid); 91 | OFCommandLine cmd; 92 | cmd.setOptionColumns(LONGCOL, SHORTCOL); 93 | cmd.setParamColumn(LONGCOL + SHORTCOL + 4); 94 | 95 | cmd.addParam("dcmfile-in", "DICOM input filename to be converted"); 96 | cmd.addParam("dcmfile-out", "DICOM output filename"); 97 | 98 | cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); 99 | cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); 100 | cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); 101 | OFLog::addOptions(cmd); 102 | 103 | #ifdef USE_LICENSE_FILE 104 | LICENSE_FILE_DECLARE_COMMAND_LINE_OPTIONS 105 | #endif 106 | 107 | cmd.addGroup("input options:"); 108 | cmd.addSubGroup("input file format:"); 109 | cmd.addOption("--read-file", "+f", "read file format or data set (default)"); 110 | cmd.addOption("--read-file-only", "+fo", "read file format only"); 111 | cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); 112 | 113 | cmd.addGroup("processing options:"); 114 | cmd.addSubGroup("planar configuration:"); 115 | cmd.addOption("--planar-restore", "+pr", "restore original planar configuration (default)"); 116 | cmd.addOption("--planar-auto", "+pa", "automatically determine planar configuration\nfrom SOP class and color space"); 117 | cmd.addOption("--color-by-pixel", "+px", "always store color-by-pixel"); 118 | cmd.addOption("--color-by-plane", "+pl", "always store color-by-plane"); 119 | cmd.addSubGroup("SOP Instance UID:"); 120 | cmd.addOption("--uid-default", "+ud", "keep same SOP Instance UID (default)"); 121 | cmd.addOption("--uid-always", "+ua", "always assign new UID"); 122 | cmd.addSubGroup("other processing options:"); 123 | cmd.addOption("--ignore-offsettable", "+io", "ignore offset table when decompressing"); 124 | 125 | cmd.addGroup("output options:"); 126 | cmd.addSubGroup("output file format:"); 127 | cmd.addOption("--write-file", "+F", "write file format (default)"); 128 | cmd.addOption("--write-dataset", "-F", "write data set without file meta information"); 129 | cmd.addSubGroup("output transfer syntax:"); 130 | cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian (default)"); 131 | cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS"); 132 | cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS"); 133 | cmd.addSubGroup("post-1993 value representations:"); 134 | cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)"); 135 | cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB"); 136 | cmd.addSubGroup("group length encoding:"); 137 | cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)"); 138 | cmd.addOption("--group-length-create", "+g", "always write with group length elements"); 139 | cmd.addOption("--group-length-remove", "-g", "always write without group length elements"); 140 | cmd.addSubGroup("length encoding in sequences and items:"); 141 | cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); 142 | cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); 143 | cmd.addSubGroup("data set trailing padding (not with --write-dataset):"); 144 | cmd.addOption("--padding-retain", "-p=", "do not change padding (default)"); 145 | cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)"); 146 | cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer", 147 | "align file on multiple of f bytes\nand items on multiple of i bytes"); 148 | 149 | /* evaluate command line */ 150 | prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); 151 | if (app.parseCommandLine(cmd, argc, argv)) 152 | { 153 | /* check exclusive options first */ 154 | if (cmd.hasExclusiveOption()) 155 | { 156 | if (cmd.findOption("--version")) 157 | { 158 | app.printHeader(OFTrue /*print host identifier*/); 159 | COUT << OFendl << "External libraries used:" << OFendl; 160 | COUT << "- " << FMJPEG2KDecoderRegistration::getLibraryVersionString() << OFendl; 161 | return 0; 162 | } 163 | } 164 | 165 | /* command line parameters */ 166 | 167 | cmd.getParam(1, opt_ifname); 168 | cmd.getParam(2, opt_ofname); 169 | 170 | /* options */ 171 | 172 | OFLog::configureFromCommandLine(cmd, app); 173 | 174 | #ifdef USE_LICENSE_FILE 175 | LICENSE_FILE_EVALUATE_COMMAND_LINE_OPTIONS 176 | #endif 177 | 178 | cmd.beginOptionBlock(); 179 | if (cmd.findOption("--planar-restore")) opt_planarconfig = EJ2KPC_restore; 180 | if (cmd.findOption("--planar-auto")) opt_planarconfig = EJ2KPC_auto; 181 | if (cmd.findOption("--color-by-pixel")) opt_planarconfig = EJ2KPC_colorByPixel; 182 | if (cmd.findOption("--color-by-plane")) opt_planarconfig = EJ2KPC_colorByPlane; 183 | cmd.endOptionBlock(); 184 | 185 | cmd.beginOptionBlock(); 186 | if (cmd.findOption("--uid-default")) opt_uidcreation = EJ2KUC_default; 187 | if (cmd.findOption("--uid-always")) opt_uidcreation = EJ2KUC_always; 188 | cmd.endOptionBlock(); 189 | 190 | if (cmd.findOption("--ignore-offsettable")) opt_ignoreOffsetTable = OFTrue; 191 | 192 | cmd.beginOptionBlock(); 193 | if (cmd.findOption("--read-file")) 194 | { 195 | opt_readMode = ERM_autoDetect; 196 | opt_ixfer = EXS_Unknown; 197 | } 198 | if (cmd.findOption("--read-file-only")) 199 | { 200 | opt_readMode = ERM_fileOnly; 201 | opt_ixfer = EXS_Unknown; 202 | } 203 | if (cmd.findOption("--read-dataset")) 204 | { 205 | opt_readMode = ERM_dataset; 206 | 207 | // This transfer syntax works as long as the content of encapsulated pixel 208 | // sequences is some kind of JPEG-LS bitstream. hmm? 209 | opt_ixfer = EXS_JPEGLSLossless; 210 | } 211 | cmd.endOptionBlock(); 212 | 213 | cmd.beginOptionBlock(); 214 | if (cmd.findOption("--write-file")) opt_writeMode = EWM_fileformat; 215 | if (cmd.findOption("--write-dataset")) opt_writeMode = EWM_dataset; 216 | cmd.endOptionBlock(); 217 | 218 | cmd.beginOptionBlock(); 219 | if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit; 220 | if (cmd.findOption("--write-xfer-big")) opt_oxfer = EXS_BigEndianExplicit; 221 | if (cmd.findOption("--write-xfer-implicit")) opt_oxfer = EXS_LittleEndianImplicit; 222 | cmd.endOptionBlock(); 223 | 224 | cmd.beginOptionBlock(); 225 | if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs(); 226 | if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs(); 227 | cmd.endOptionBlock(); 228 | 229 | cmd.beginOptionBlock(); 230 | if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL; 231 | if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL; 232 | if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL; 233 | cmd.endOptionBlock(); 234 | 235 | cmd.beginOptionBlock(); 236 | if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength; 237 | if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength; 238 | cmd.endOptionBlock(); 239 | 240 | cmd.beginOptionBlock(); 241 | if (cmd.findOption("--padding-retain")) 242 | { 243 | app.checkConflict("--padding-retain", "--write-dataset", opt_writeMode == EWM_dataset); 244 | opt_opadenc = EPD_noChange; 245 | } 246 | if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding; 247 | if (cmd.findOption("--padding-create")) 248 | { 249 | app.checkConflict("--padding-create", "--write-dataset", opt_writeMode == EWM_dataset); 250 | app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0)); 251 | app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0)); 252 | opt_opadenc = EPD_withPadding; 253 | } 254 | cmd.endOptionBlock(); 255 | } 256 | 257 | /* print resource identifier */ 258 | OFLOG_DEBUG(dcmdjplsLogger, rcsid << OFendl); 259 | 260 | // register global decompression codecs 261 | FMJPEG2KDecoderRegistration::registerCodecs(opt_uidcreation, opt_planarconfig, opt_ignoreOffsetTable); 262 | 263 | /* make sure data dictionary is loaded */ 264 | if (!dcmDataDict.isDictionaryLoaded()) 265 | { 266 | OFLOG_WARN(dcmdjplsLogger, "no data dictionary loaded, " 267 | << "check environment variable: " 268 | << DCM_DICT_ENVIRONMENT_VARIABLE); 269 | } 270 | 271 | // open input file 272 | if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) 273 | { 274 | OFLOG_FATAL(dcmdjplsLogger, "invalid filename: "); 275 | return 1; 276 | } 277 | 278 | DcmFileFormat fileformat; 279 | 280 | OFLOG_INFO(dcmdjplsLogger, "reading input file " << opt_ifname); 281 | 282 | OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); 283 | if (error.bad()) 284 | { 285 | OFLOG_FATAL(dcmdjplsLogger, error.text() << ": reading file: " << opt_ifname); 286 | return 1; 287 | } 288 | 289 | DcmDataset *dataset = fileformat.getDataset(); 290 | 291 | OFLOG_INFO(dcmdjplsLogger, "decompressing file"); 292 | 293 | DcmXfer opt_oxferSyn(opt_oxfer); 294 | DcmXfer original_xfer(dataset->getOriginalXfer()); 295 | 296 | error = dataset->chooseRepresentation(opt_oxfer, NULL); 297 | if (error.bad()) 298 | { 299 | OFLOG_FATAL(dcmdjplsLogger, error.text() << ": decompressing file: " << opt_ifname); 300 | if (error == EC_CannotChangeRepresentation) 301 | OFLOG_FATAL(dcmdjplsLogger, "Input transfer syntax " << original_xfer.getXferName() << " not supported"); 302 | return 1; 303 | } 304 | 305 | if (! dataset->canWriteXfer(opt_oxfer)) 306 | { 307 | OFLOG_FATAL(dcmdjplsLogger, "no conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible"); 308 | return 1; 309 | } 310 | 311 | OFLOG_INFO(dcmdjplsLogger, "creating output file " << opt_ofname); 312 | 313 | // update file meta information with new SOP Instance UID 314 | if (opt_uidcreation && (opt_writeMode == EWM_fileformat)) 315 | opt_writeMode = EWM_updateMeta; 316 | 317 | fileformat.loadAllDataIntoMemory(); 318 | error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc, 319 | OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), opt_writeMode); 320 | 321 | if (error != EC_Normal) 322 | { 323 | OFLOG_FATAL(dcmdjplsLogger, error.text() << ": writing file: " << opt_ofname); 324 | return 1; 325 | } 326 | 327 | OFLOG_INFO(dcmdjplsLogger, "conversion successful"); 328 | 329 | // deregister global decompression codecs 330 | FMJPEG2KDecoderRegistration::cleanup(); 331 | 332 | return 0; 333 | } 334 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djcodecd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: codec classes for JPEG-2000 decoders. 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJCODECD_H 19 | #define FMJPEG2K_DJCODECD_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/dcmdata/dccodec.h" /* for class DcmCodec */ 23 | #include "dcmtk/ofstd/ofstring.h" 24 | #include "dldefine.h" 25 | 26 | /* forward declaration */ 27 | class DJPEG2KCodecParameter; 28 | 29 | /** abstract codec class for JPEG-2000 decoders. 30 | * This abstract class contains most of the application logic 31 | * needed for a dcmdata codec object that implements a JPEG-2000 decoder. 32 | * This class only supports decompression, it neither implements 33 | * encoding nor transcoding. 34 | */ 35 | class FMJPEG2K_EXPORT DJPEG2KDecoderBase: public DcmCodec 36 | { 37 | public: 38 | 39 | /// default constructor 40 | DJPEG2KDecoderBase(); 41 | 42 | /// destructor 43 | virtual ~DJPEG2KDecoderBase(); 44 | 45 | /** decompresses the given pixel sequence and 46 | * stores the result in the given uncompressedPixelData element. 47 | * @param fromRepParam current representation parameter of compressed data, may be NULL 48 | * @param pixSeq compressed pixel sequence 49 | * @param uncompressedPixelData uncompressed pixel data stored in this element 50 | * @param cp codec parameters for this codec 51 | * @param objStack stack pointing to the location of the pixel data 52 | * element in the current dataset. 53 | * @return EC_Normal if successful, an error code otherwise. 54 | */ 55 | virtual OFCondition decode( 56 | const DcmRepresentationParameter * fromRepParam, 57 | DcmPixelSequence * pixSeq, 58 | DcmPolymorphOBOW& uncompressedPixelData, 59 | const DcmCodecParameter * cp, 60 | const DcmStack& objStack) const; 61 | 62 | /** decompresses the given pixel sequence and 63 | * stores the result in the given uncompressedPixelData element. 64 | * @param fromRepParam current representation parameter of compressed data, may be NULL 65 | * @param pixSeq compressed pixel sequence 66 | * @param uncompressedPixelData uncompressed pixel data stored in this element 67 | * @param cp codec parameters for this codec 68 | * @param objStack stack pointing to the location of the pixel data 69 | * element in the current dataset. 70 | * @param removeOldRep boolean flag that should be set to false before this method call 71 | * and will be set to true if the codec modifies the DICOM dataset such 72 | * that the pixel data of the original representation may not be usable 73 | * anymore. 74 | * @return EC_Normal if successful, an error code otherwise. 75 | */ 76 | virtual OFCondition decode( 77 | const DcmRepresentationParameter * fromRepParam, 78 | DcmPixelSequence * pixSeq, 79 | DcmPolymorphOBOW& uncompressedPixelData, 80 | const DcmCodecParameter * cp, 81 | const DcmStack& objStack, 82 | OFBool& removeOldRep) const; 83 | 84 | /** decompresses a single frame from the given pixel sequence and 85 | * stores the result in the given buffer. 86 | * @param fromParam representation parameter of current compressed 87 | * representation, may be NULL. 88 | * @param fromPixSeq compressed pixel sequence 89 | * @param cp codec parameters for this codec 90 | * @param dataset pointer to dataset in which pixel data element is contained 91 | * @param frameNo number of frame, starting with 0 for the first frame 92 | * @param startFragment index of the compressed fragment that contains 93 | * all or the first part of the compressed bitstream for the given frameNo. 94 | * Upon successful return this parameter is updated to contain the index 95 | * of the first compressed fragment of the next frame. 96 | * When unknown, zero should be passed. In this case the decompression 97 | * algorithm will try to determine the index by itself, which will always 98 | * work if frames are decompressed in increasing order from first to last, 99 | * but may fail if frames are decompressed in random order, multiple fragments 100 | * per frame and multiple frames are present in the dataset, and the offset 101 | * table is empty. 102 | * @param buffer pointer to buffer where frame is to be stored 103 | * @param bufSize size of buffer in bytes 104 | * @param decompressedColorModel upon successful return, the color model 105 | * of the decompressed image (which may be different from the one used 106 | * in the compressed images) is returned in this parameter. 107 | * @return EC_Normal if successful, an error code otherwise. 108 | */ 109 | virtual OFCondition decodeFrame( 110 | const DcmRepresentationParameter * fromParam, 111 | DcmPixelSequence * fromPixSeq, 112 | const DcmCodecParameter * cp, 113 | DcmItem *dataset, 114 | Uint32 frameNo, 115 | Uint32& startFragment, 116 | void *buffer, 117 | Uint32 bufSize, 118 | OFString& decompressedColorModel) const; 119 | 120 | /** compresses the given uncompressed DICOM image and stores 121 | * the result in the given pixSeq element. 122 | * @param pixelData pointer to the uncompressed image data in OW format 123 | * and local byte order 124 | * @param length of the pixel data field in bytes 125 | * @param toRepParam representation parameter describing the desired 126 | * compressed representation (e.g. JPEG quality) 127 | * @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 128 | * allocated on heap) returned in this parameter upon success. 129 | * @param cp codec parameters for this codec 130 | * @param objStack stack pointing to the location of the pixel data 131 | * element in the current dataset. 132 | * @return EC_Normal if successful, an error code otherwise. 133 | */ 134 | virtual OFCondition encode( 135 | const Uint16 * pixelData, 136 | const Uint32 length, 137 | const DcmRepresentationParameter * toRepParam, 138 | DcmPixelSequence * & pixSeq, 139 | const DcmCodecParameter *cp, 140 | DcmStack & objStack) const; 141 | 142 | /** compresses the given uncompressed DICOM image and stores 143 | * the result in the given pixSeq element. 144 | * @param pixelData pointer to the uncompressed image data in OW format 145 | * and local byte order 146 | * @param length of the pixel data field in bytes 147 | * @param toRepParam representation parameter describing the desired 148 | * compressed representation (e.g. JPEG quality) 149 | * @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 150 | * allocated on heap) returned in this parameter upon success. 151 | * @param cp codec parameters for this codec 152 | * @param objStack stack pointing to the location of the pixel data 153 | * element in the current dataset. 154 | * @param removeOldRep boolean flag that should be set to false before this method call 155 | * and will be set to true if the codec modifies the DICOM dataset such 156 | * that the pixel data of the original representation may not be usable 157 | * anymore. 158 | * @return EC_Normal if successful, an error code otherwise. 159 | */ 160 | virtual OFCondition encode( 161 | const Uint16 * pixelData, 162 | const Uint32 length, 163 | const DcmRepresentationParameter * toRepParam, 164 | DcmPixelSequence * & pixSeq, 165 | const DcmCodecParameter *cp, 166 | DcmStack & objStack, 167 | OFBool& removeOldRep) const; 168 | 169 | /** transcodes (re-compresses) the given compressed DICOM image and stores 170 | * the result in the given toPixSeq element. 171 | * @param fromRepType current transfer syntax of the compressed image 172 | * @param fromRepParam current representation parameter of compressed data, may be NULL 173 | * @param fromPixSeq compressed pixel sequence 174 | * @param toRepParam representation parameter describing the desired 175 | * new compressed representation (e.g. JPEG quality) 176 | * @param toPixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 177 | * allocated on heap) returned in this parameter upon success. 178 | * @param cp codec parameters for this codec 179 | * @param objStack stack pointing to the location of the pixel data 180 | * element in the current dataset. 181 | * @return EC_Normal if successful, an error code otherwise. 182 | */ 183 | virtual OFCondition encode( 184 | const E_TransferSyntax fromRepType, 185 | const DcmRepresentationParameter * fromRepParam, 186 | DcmPixelSequence * fromPixSeq, 187 | const DcmRepresentationParameter * toRepParam, 188 | DcmPixelSequence * & toPixSeq, 189 | const DcmCodecParameter * cp, 190 | DcmStack & objStack) const; 191 | 192 | /** transcodes (re-compresses) the given compressed DICOM image and stores 193 | * the result in the given toPixSeq element. 194 | * @param fromRepType current transfer syntax of the compressed image 195 | * @param fromRepParam current representation parameter of compressed data, may be NULL 196 | * @param fromPixSeq compressed pixel sequence 197 | * @param toRepParam representation parameter describing the desired 198 | * new compressed representation (e.g. JPEG quality) 199 | * @param toPixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 200 | * allocated on heap) returned in this parameter upon success. 201 | * @param cp codec parameters for this codec 202 | * @param objStack stack pointing to the location of the pixel data 203 | * element in the current dataset. 204 | * @param removeOldRep boolean flag that should be set to false before this method call 205 | * and will be set to true if the codec modifies the DICOM dataset such 206 | * that the pixel data of the original representation may not be usable 207 | * anymore. 208 | * @return EC_Normal if successful, an error code otherwise. 209 | */ 210 | virtual OFCondition encode( 211 | const E_TransferSyntax fromRepType, 212 | const DcmRepresentationParameter * fromRepParam, 213 | DcmPixelSequence * fromPixSeq, 214 | const DcmRepresentationParameter * toRepParam, 215 | DcmPixelSequence * & toPixSeq, 216 | const DcmCodecParameter * cp, 217 | DcmStack & objStack, 218 | OFBool& removeOldRep) const; 219 | 220 | /** checks if this codec is able to convert from the 221 | * given current transfer syntax to the given new 222 | * transfer syntax 223 | * @param oldRepType current transfer syntax 224 | * @param newRepType desired new transfer syntax 225 | * @return true if transformation is supported by this codec, false otherwise. 226 | */ 227 | virtual OFBool canChangeCoding( 228 | const E_TransferSyntax oldRepType, 229 | const E_TransferSyntax newRepType) const; 230 | 231 | /** determine color model of the decompressed image 232 | * @param fromParam representation parameter of current compressed 233 | * representation, may be NULL 234 | * @param fromPixSeq compressed pixel sequence 235 | * @param cp codec parameters for this codec 236 | * @param dataset pointer to dataset in which pixel data element is contained 237 | * @param dataset pointer to DICOM dataset in which this pixel data object 238 | * is located. Used to access photometric interpretation. 239 | * @param decompressedColorModel upon successful return, the color model 240 | * of the decompressed image (which may be different from the one used 241 | * in the compressed images) is returned in this parameter 242 | * @return EC_Normal if successful, an error code otherwise 243 | */ 244 | virtual OFCondition determineDecompressedColorModel( 245 | const DcmRepresentationParameter *fromParam, 246 | DcmPixelSequence *fromPixSeq, 247 | const DcmCodecParameter *cp, 248 | DcmItem *dataset, 249 | OFString &decompressedColorModel) const; 250 | 251 | private: 252 | 253 | // static private helper methods 254 | 255 | /** decompresses a single frame from the given pixel sequence and 256 | * stores the result in the given buffer. 257 | * @param fromPixSeq compressed pixel sequence 258 | * @param cp codec parameters for this codec 259 | * @param dataset pointer to dataset in which pixel data element is contained 260 | * @param frameNo number of frame, starting with 0 for the first frame 261 | * @param startFragment index of the compressed fragment that contains 262 | * all or the first part of the compressed bitstream for the given frameNo. 263 | * Upon successful return this parameter is updated to contain the index 264 | * of the first compressed fragment of the next frame. 265 | * When unknown, zero should be passed. In this case the decompression 266 | * algorithm will try to determine the index by itself, which will always 267 | * work if frames are decompressed in increasing order from first to last, 268 | * but may fail if frames are decompressed in random order, multiple fragments 269 | * per frame and multiple frames are present in the dataset, and the offset 270 | * table is empty. 271 | * @param buffer pointer to buffer where frame is to be stored 272 | * @param bufSize size of buffer in bytes 273 | * @param imageFrames number of frames in this image 274 | * @param imageColumns number of columns for each frame 275 | * @param imageRows number of rows for each frame 276 | * @param imageSamplesPerPixel number of samples per pixel 277 | * @param bytesPerSample number of bytes per sample 278 | * @return EC_Normal if successful, an error code otherwise. 279 | */ 280 | static OFCondition decodeFrame( 281 | DcmPixelSequence * fromPixSeq, 282 | const DJPEG2KCodecParameter *cp, 283 | DcmItem *dataset, 284 | Uint32 frameNo, 285 | Uint32& startFragment, 286 | void *buffer, 287 | Uint32 bufSize, 288 | Sint32 imageFrames, 289 | Uint16 imageColumns, 290 | Uint16 imageRows, 291 | Uint16 imageSamplesPerPixel, 292 | Uint16 bytesPerSample); 293 | 294 | /** determines if a given image requires color-by-plane planar configuration 295 | * depending on SOP Class UID (DICOM IOD) and photometric interpretation. 296 | * All SOP classes defined in the 2003 edition of the DICOM standard or earlier 297 | * are handled correctly. 298 | * @param sopClassUID SOP Class UID 299 | * @param photometricInterpretation decompressed photometric interpretation 300 | * @return legal value for planar configuration 301 | */ 302 | static Uint16 determinePlanarConfiguration( 303 | const OFString& sopClassUID, 304 | const OFString& photometricInterpretation); 305 | 306 | /** computes the number of fragments (pixel items) that comprise the current 307 | * frame in the compressed pixel sequence. This method uses various approaches 308 | * to compute the number of fragments for a frame, including a check of the 309 | * offset table and checking the start of each fragment for JPEG SOI markers. 310 | * @param numberOfFrames total number of frames of the DICOM object 311 | * @param currentFrame index of current frame (0..numberOfFrames-1) 312 | * @param startItem index of fragment (pixel item) the frame starts with 313 | * @param ignoreOffsetTable flag instructing the method to ignore the offset table 314 | * even if present and presumably useful 315 | * @param pixSeq the compressed JPEG-2000 pixel sequence 316 | * @return number of fragments for the current frame, zero upon error 317 | */ 318 | static Uint32 computeNumberOfFragments( 319 | Sint32 numberOfFrames, 320 | Uint32 currentFrame, 321 | Uint32 startItem, 322 | OFBool ignoreOffsetTable, 323 | DcmPixelSequence *pixSeq); 324 | 325 | /** check whether the given buffer contains a JPEG-2000 start-of-image code 326 | * @param fragmentData pointer to 4 or more bytes of JPEG-2000 data 327 | * @returns true if the first four bytes of the code stream indicate that 328 | * this fragment is the start of a new JPEG-2000 image, i.e. starts with 329 | * an SOI marker followed by SOF, COM or APPn. 330 | */ 331 | static OFBool isJPEGLSStartOfImage(Uint8 *fragmentData); 332 | 333 | /** converts an RGB or YBR frame with 8 bits/sample from 334 | * color-by-pixel to color-by-plane planar configuration. 335 | * @param imageFrame pointer to image frame, must contain 336 | * at least 3*columns*rows bytes of pixel data. 337 | * @param columns columns 338 | * @param rows rows 339 | * @return EC_Normal if successful, an error code otherwise 340 | */ 341 | static OFCondition createPlanarConfiguration1Byte( 342 | Uint8 *imageFrame, 343 | Uint16 columns, 344 | Uint16 rows); 345 | 346 | /** converts an RGB or YBR frame with 16 bits/sample from 347 | * color-by-pixel to color-by-plane planar configuration. 348 | * @param imageFrame pointer to image frame, must contain 349 | * at least 3*columns*rows words of pixel data. 350 | * @param columns columns 351 | * @param rows rows 352 | * @return EC_Normal if successful, an error code otherwise 353 | */ 354 | static OFCondition createPlanarConfiguration1Word( 355 | Uint16 *imageFrame, 356 | Uint16 columns, 357 | Uint16 rows); 358 | 359 | /** converts an RGB or YBR frame with 8 bits/sample from 360 | * color-by-plane to color-by-pixel planar configuration. 361 | * @param imageFrame pointer to image frame, must contain 362 | * at least 3*columns*rows bytes of pixel data. 363 | * @param columns columns 364 | * @param rows rows 365 | * @return EC_Normal if successful, an error code otherwise 366 | */ 367 | static OFCondition createPlanarConfiguration0Byte( 368 | Uint8 *imageFrame, 369 | Uint16 columns, 370 | Uint16 rows); 371 | 372 | /** converts an RGB or YBR frame with 16 bits/sample from 373 | * color-by-plane to color-by-pixel planar configuration. 374 | * @param imageFrame pointer to image frame, must contain 375 | * at least 3*columns*rows words of pixel data. 376 | * @param columns columns 377 | * @param rows rows 378 | * @return EC_Normal if successful, an error code otherwise 379 | */ 380 | static OFCondition createPlanarConfiguration0Word( 381 | Uint16 *imageFrame, 382 | Uint16 columns, 383 | Uint16 rows); 384 | }; 385 | 386 | /** codec class for JPEG-2000 lossy and lossless TS decoding 387 | */ 388 | class FMJPEG2K_EXPORT DJPEG2KDecoder : public DJPEG2KDecoderBase 389 | { 390 | 391 | }; 392 | 393 | #endif 394 | -------------------------------------------------------------------------------- /dcmcjp2k.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2007-2014, OFFIS e.V. 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * OFFIS e.V. 9 | * R&D Division Health 10 | * Escherweg 2 11 | * D-26121 Oldenburg, Germany 12 | * 13 | * 14 | * Module: dcmcjpls 15 | * 16 | * Author: Martin Willkomm, Uli Schlachter 17 | * 18 | * Purpose: Compress DICOM file with JPEG-LS transfer syntax 19 | * 20 | */ 21 | 22 | #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "dcmtk/ofstd/ofstdinc.h" 28 | 29 | #ifdef HAVE_GUSI_H 30 | #include 31 | #endif 32 | 33 | #include "dcmtk/dcmdata/dctk.h" 34 | #include "dcmtk/dcmdata/cmdlnarg.h" 35 | #include "dcmtk/ofstd/ofconapp.h" 36 | #include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ 37 | #include "dcmtk/dcmimage/diregist.h" /* include to support color images */ 38 | #include "fmjpeg2k/djencode.h" /* for class DJLSEncoderRegistration */ 39 | #include "fmjpeg2k/djrparam.h" /* for class FMJP2KRepresentationParameter */ 40 | 41 | #ifdef USE_LICENSE_FILE 42 | #include "oflice.h" 43 | #endif 44 | 45 | #ifndef OFFIS_CONSOLE_APPLICATION 46 | #define OFFIS_CONSOLE_APPLICATION "fmcjp2k" 47 | #endif 48 | 49 | static OFLogger fmjp2kLogger = OFLog::getLogger(OFFIS_CONSOLE_APPLICATION); 50 | 51 | static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" 52 | OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; 53 | 54 | // ******************************************** 55 | 56 | 57 | #define SHORTCOL 3 58 | #define LONGCOL 21 59 | 60 | int main(int argc, char *argv[]) 61 | { 62 | 63 | #ifdef HAVE_GUSI_H 64 | GUSISetup(GUSIwithSIOUXSockets); 65 | GUSISetup(GUSIwithInternetSockets); 66 | #endif 67 | 68 | const char *opt_ifname = NULL; 69 | const char *opt_ofname = NULL; 70 | 71 | // input options 72 | E_FileReadMode opt_readMode = ERM_autoDetect; 73 | E_TransferSyntax opt_ixfer = EXS_Unknown; 74 | 75 | // JPEG-LS encoding options 76 | E_TransferSyntax opt_oxfer = EXS_JPEG2000LosslessOnly; 77 | OFBool opt_useLosslessProcess = OFTrue; 78 | 79 | OFCmdUnsignedInt opt_cblkwidth = 64; 80 | OFCmdUnsignedInt opt_cblkheight = 64; 81 | 82 | OFBool opt_use_custom_options = OFFalse; 83 | 84 | // JPEG-LS options 85 | OFCmdUnsignedInt opt_nearlossless_psnr = 0; 86 | OFBool opt_prefer_cooked = OFFalse; 87 | 88 | // encapsulated pixel data encoding options 89 | OFCmdUnsignedInt opt_fragmentSize = 0; // 0=unlimited 90 | OFBool opt_createOffsetTable = OFTrue; 91 | J2K_UIDCreation opt_uidcreation = EJ2KUC_default; 92 | OFBool opt_secondarycapture = OFFalse; 93 | 94 | // output options 95 | E_GrpLenEncoding opt_oglenc = EGL_recalcGL; 96 | E_EncodingType opt_oenctype = EET_ExplicitLength; 97 | E_PaddingEncoding opt_opadenc = EPD_noChange; 98 | OFCmdUnsignedInt opt_filepad = 0; 99 | OFCmdUnsignedInt opt_itempad = 0; 100 | 101 | #ifdef USE_LICENSE_FILE 102 | LICENSE_FILE_DECLARATIONS 103 | #endif 104 | 105 | OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encode DICOM file to JPEG-LS transfer syntax", rcsid); 106 | OFCommandLine cmd; 107 | cmd.setOptionColumns(LONGCOL, SHORTCOL); 108 | cmd.setParamColumn(LONGCOL + SHORTCOL + 4); 109 | 110 | cmd.addParam("dcmfile-in", "DICOM input filename to be converted"); 111 | cmd.addParam("dcmfile-out", "DICOM output filename"); 112 | 113 | cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); 114 | cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); 115 | cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); 116 | OFLog::addOptions(cmd); 117 | 118 | #ifdef USE_LICENSE_FILE 119 | LICENSE_FILE_DECLARE_COMMAND_LINE_OPTIONS 120 | #endif 121 | 122 | cmd.addGroup("input options:"); 123 | cmd.addSubGroup("input file format:"); 124 | cmd.addOption("--read-file", "+f", "read file format or data set (default)"); 125 | cmd.addOption("--read-file-only", "+fo", "read file format only"); 126 | cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); 127 | cmd.addSubGroup("input transfer syntax:"); 128 | cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); 129 | cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); 130 | cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); 131 | cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); 132 | cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); 133 | 134 | cmd.addGroup("JPEG-2000 encoding options:"); 135 | cmd.addSubGroup("JPEG-2000 process:"); 136 | cmd.addOption("--encode-lossless", "+el", "encode JPEG-2000 lossless (default)"); 137 | cmd.addOption("--encode-nearlossless", "+en", "encode JPEG-2000 TS near-lossless"); 138 | 139 | cmd.addSubGroup("lossless compression:"); 140 | cmd.addOption("--prefer-raw", "+pr", "prefer raw encoder mode (default)"); 141 | cmd.addOption("--prefer-cooked", "+pc", "prefer cooked encoder mode "); 142 | 143 | cmd.addSubGroup("JPEG-2000 compression:"); 144 | cmd.addOption("--PSNR", "+ps", 1, "[s]ize: integer (default: 0 (automatic))", 145 | "set JPEG-2000 encoding parameter psnr"); 146 | cmd.addOption("--cblkwidth", "+cw", 1, "[s]ize: integer (default: 64)", 147 | "set JPEG-2000 encoding parameter code block size"); 148 | cmd.addOption("--cblkheight", "+ch", 1, "[s]ize: integer (default: 64)", 149 | "set JPEG-2000 encoding parameter code block size"); 150 | 151 | cmd.addGroup("encapsulated pixel data encoding options:"); 152 | cmd.addSubGroup("pixel data fragmentation:"); 153 | cmd.addOption("--fragment-per-frame", "+ff", "encode each frame as one fragment (default)"); 154 | cmd.addOption("--fragment-size", "+fs", 1, "[s]ize: integer", 155 | "limit fragment size to s kbytes"); 156 | cmd.addSubGroup("basic offset table encoding:"); 157 | cmd.addOption("--offset-table-create", "+ot", "create offset table (default)"); 158 | cmd.addOption("--offset-table-empty", "-ot", "leave offset table empty"); 159 | cmd.addSubGroup("SOP Class UID:"); 160 | cmd.addOption("--class-default", "+cd", "keep SOP Class UID (default)"); 161 | cmd.addOption("--class-sc", "+cs", "convert to Secondary Capture Image\n(implies --uid-always)"); 162 | cmd.addSubGroup("SOP Instance UID:"); 163 | cmd.addOption("--uid-default", "+ud", "assign new UID if lossy compression (default)"); 164 | cmd.addOption("--uid-always", "+ua", "always assign new UID"); 165 | cmd.addOption("--uid-never", "+un", "never assign new UID"); 166 | 167 | cmd.addGroup("output options:"); 168 | cmd.addSubGroup("post-1993 value representations:"); 169 | cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)"); 170 | cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB"); 171 | cmd.addSubGroup("group length encoding:"); 172 | cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)"); 173 | cmd.addOption("--group-length-create", "+g", "always write with group length elements"); 174 | cmd.addOption("--group-length-remove", "-g", "always write without group length elements"); 175 | cmd.addSubGroup("length encoding in sequences and items:"); 176 | cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); 177 | cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); 178 | cmd.addSubGroup("data set trailing padding:"); 179 | cmd.addOption("--padding-retain", "-p=", "do not change padding (default)"); 180 | cmd.addOption("--padding-off", "-p", "no padding"); 181 | cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer", 182 | "align file on multiple of f bytes\nand items on multiple of i byte"); 183 | /* evaluate command line */ 184 | prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); 185 | if (app.parseCommandLine(cmd, argc, argv)) 186 | { 187 | /* check exclusive options first */ 188 | if (cmd.hasExclusiveOption()) 189 | { 190 | if (cmd.findOption("--version")) 191 | { 192 | app.printHeader(OFTrue /*print host identifier*/); 193 | COUT << OFendl << "External libraries used:" << OFendl; 194 | COUT << "- " << FMJPEG2KEncoderRegistration::getLibraryVersionString() << OFendl; 195 | return 0; 196 | } 197 | } 198 | 199 | /* command line parameters */ 200 | 201 | cmd.getParam(1, opt_ifname); 202 | cmd.getParam(2, opt_ofname); 203 | 204 | // general options 205 | OFLog::configureFromCommandLine(cmd, app); 206 | 207 | #ifdef USE_LICENSE_FILE 208 | LICENSE_FILE_EVALUATE_COMMAND_LINE_OPTIONS 209 | #endif 210 | 211 | // input options 212 | // input file format 213 | cmd.beginOptionBlock(); 214 | if (cmd.findOption("--read-file")) opt_readMode = ERM_autoDetect; 215 | if (cmd.findOption("--read-file-only")) opt_readMode = ERM_fileOnly; 216 | if (cmd.findOption("--read-dataset")) opt_readMode = ERM_dataset; 217 | cmd.endOptionBlock(); 218 | 219 | // input transfer syntax 220 | cmd.beginOptionBlock(); 221 | if (cmd.findOption("--read-xfer-auto")) 222 | opt_ixfer = EXS_Unknown; 223 | if (cmd.findOption("--read-xfer-detect")) 224 | dcmAutoDetectDatasetXfer.set(OFTrue); 225 | if (cmd.findOption("--read-xfer-little")) 226 | { 227 | app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset); 228 | opt_ixfer = EXS_LittleEndianExplicit; 229 | } 230 | if (cmd.findOption("--read-xfer-big")) 231 | { 232 | app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset); 233 | opt_ixfer = EXS_BigEndianExplicit; 234 | } 235 | if (cmd.findOption("--read-xfer-implicit")) 236 | { 237 | app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset); 238 | opt_ixfer = EXS_LittleEndianImplicit; 239 | } 240 | cmd.endOptionBlock(); 241 | 242 | // JPEG-LS encoding options 243 | // JPEG-LS process options 244 | cmd.beginOptionBlock(); 245 | if (cmd.findOption("--encode-lossless")) 246 | { 247 | opt_oxfer = EXS_JPEG2000LosslessOnly; 248 | opt_useLosslessProcess = OFTrue; 249 | } 250 | if (cmd.findOption("--encode-nearlossless")) 251 | { 252 | opt_oxfer = EXS_JPEG2000; 253 | opt_useLosslessProcess = OFFalse; 254 | opt_nearlossless_psnr = 0; // 0 for automatic 255 | } 256 | cmd.endOptionBlock(); 257 | 258 | // JPEG-2000 psnr options 259 | if (cmd.findOption("--PSNR")) 260 | { 261 | app.checkConflict("--PSNR", "--encode-lossless", opt_oxfer == EXS_JPEG2000LosslessOnly); 262 | app.checkValue(cmd.getValueAndCheckMin(opt_nearlossless_psnr, 0)); 263 | } 264 | 265 | // lossless compression options 266 | cmd.beginOptionBlock(); 267 | if (cmd.findOption("--prefer-raw")) 268 | { 269 | opt_prefer_cooked = OFFalse; 270 | } 271 | if (cmd.findOption("--prefer-cooked")) 272 | { 273 | opt_prefer_cooked = OFTrue; 274 | } 275 | cmd.endOptionBlock(); 276 | 277 | // thresholds and reset 278 | cmd.beginOptionBlock(); 279 | if (cmd.findOption("--cblkwidth")) 280 | { 281 | app.checkValue(cmd.getValueAndCheckMinMax(opt_cblkwidth, OFstatic_cast(OFCmdUnsignedInt, 4), 1024)); 282 | opt_use_custom_options = OFTrue; 283 | } 284 | if (cmd.findOption("--cblkheight")) 285 | { 286 | app.checkValue(cmd.getValueAndCheckMinMax(opt_cblkheight, OFstatic_cast(OFCmdUnsignedInt, 4), 1024)); 287 | opt_use_custom_options = OFTrue; 288 | } 289 | cmd.endOptionBlock(); 290 | 291 | // encapsulated pixel data encoding options 292 | // pixel data fragmentation options 293 | cmd.beginOptionBlock(); 294 | if (cmd.findOption("--fragment-per-frame")) opt_fragmentSize = 0; 295 | if (cmd.findOption("--fragment-size")) 296 | { 297 | app.checkValue(cmd.getValueAndCheckMin(opt_fragmentSize, OFstatic_cast(OFCmdUnsignedInt, 1))); 298 | } 299 | cmd.endOptionBlock(); 300 | 301 | // basic offset table encoding options 302 | cmd.beginOptionBlock(); 303 | if (cmd.findOption("--offset-table-create")) opt_createOffsetTable = OFTrue; 304 | if (cmd.findOption("--offset-table-empty")) opt_createOffsetTable = OFFalse; 305 | cmd.endOptionBlock(); 306 | 307 | // SOP Class UID options 308 | cmd.beginOptionBlock(); 309 | if (cmd.findOption("--class-default")) opt_secondarycapture = OFFalse; 310 | if (cmd.findOption("--class-sc")) opt_secondarycapture = OFTrue; 311 | cmd.endOptionBlock(); 312 | 313 | // SOP Instance UID options 314 | cmd.beginOptionBlock(); 315 | if (cmd.findOption("--uid-default")) opt_uidcreation = EJ2KUC_default; 316 | if (cmd.findOption("--uid-always")) opt_uidcreation = EJ2KUC_always; 317 | if (cmd.findOption("--uid-never")) opt_uidcreation = EJ2KUC_never; 318 | cmd.endOptionBlock(); 319 | 320 | // output options 321 | // post-1993 value representations 322 | cmd.beginOptionBlock(); 323 | if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs(); 324 | if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs(); 325 | cmd.endOptionBlock(); 326 | 327 | // group length encoding 328 | cmd.beginOptionBlock(); 329 | if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL; 330 | if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL; 331 | if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL; 332 | cmd.endOptionBlock(); 333 | 334 | // length encoding in sequences and items 335 | cmd.beginOptionBlock(); 336 | if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength; 337 | if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength; 338 | cmd.endOptionBlock(); 339 | 340 | // data set trailing padding 341 | cmd.beginOptionBlock(); 342 | if (cmd.findOption("--padding-retain")) opt_opadenc = EPD_noChange; 343 | if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding; 344 | if (cmd.findOption("--padding-create")) 345 | { 346 | app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0)); 347 | app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0)); 348 | opt_opadenc = EPD_withPadding; 349 | } 350 | cmd.endOptionBlock(); 351 | } 352 | 353 | /* print resource identifier */ 354 | OFLOG_DEBUG(fmjp2kLogger, rcsid << OFendl); 355 | 356 | // register global compression codecs 357 | FMJPEG2KEncoderRegistration::registerCodecs(opt_use_custom_options, 358 | OFstatic_cast(Uint16, opt_cblkwidth), OFstatic_cast(Uint16, opt_cblkheight), 359 | opt_prefer_cooked, opt_fragmentSize, opt_createOffsetTable, 360 | opt_uidcreation, opt_secondarycapture); 361 | 362 | /* make sure data dictionary is loaded */ 363 | if (!dcmDataDict.isDictionaryLoaded()) 364 | { 365 | OFLOG_WARN(fmjp2kLogger, "no data dictionary loaded, " 366 | << "check environment variable: " 367 | << DCM_DICT_ENVIRONMENT_VARIABLE); 368 | } 369 | 370 | // open inputfile 371 | if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) 372 | { 373 | OFLOG_FATAL(fmjp2kLogger, "invalid filename: "); 374 | return 1; 375 | } 376 | 377 | OFLOG_INFO(fmjp2kLogger, "reading input file " << opt_ifname); 378 | 379 | DcmFileFormat fileformat; 380 | OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); 381 | if (error.bad()) 382 | { 383 | OFLOG_FATAL(fmjp2kLogger, error.text() << ": reading file: " << opt_ifname); 384 | return 1; 385 | } 386 | DcmDataset *dataset = fileformat.getDataset(); 387 | 388 | DcmXfer original_xfer(dataset->getOriginalXfer()); 389 | if (original_xfer.isEncapsulated()) 390 | { 391 | OFLOG_INFO(fmjp2kLogger, "DICOM file is already compressed, converting to uncompressed transfer syntax first"); 392 | if (EC_Normal != dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL)) 393 | { 394 | OFLOG_FATAL(fmjp2kLogger, "No conversion from compressed original to uncompressed transfer syntax possible!"); 395 | return 1; 396 | } 397 | } 398 | 399 | OFString sopClass; 400 | if (fileformat.getMetaInfo()->findAndGetOFString(DCM_MediaStorageSOPClassUID, sopClass).good()) 401 | { 402 | /* check for DICOMDIR files */ 403 | if (sopClass == UID_MediaStorageDirectoryStorage) 404 | { 405 | OFLOG_FATAL(fmjp2kLogger, "DICOMDIR files (Media Storage Directory Storage SOP Class) cannot be compressed!"); 406 | return 1; 407 | } 408 | } 409 | 410 | OFLOG_INFO(fmjp2kLogger, "Convert DICOM file to compressed transfer syntax"); 411 | 412 | //create representation parameter 413 | FMJPEG2KRepresentationParameter rp(OFstatic_cast(Uint16, opt_nearlossless_psnr), opt_useLosslessProcess); 414 | DcmXfer opt_oxferSyn(opt_oxfer); 415 | 416 | // perform decoding process 417 | OFCondition result = dataset->chooseRepresentation(opt_oxfer, &rp); 418 | if (result.bad()) 419 | { 420 | OFLOG_FATAL(fmjp2kLogger, result.text() << ": encoding file: " << opt_ifname); 421 | return 1; 422 | } 423 | if (dataset->canWriteXfer(opt_oxfer)) 424 | { 425 | OFLOG_INFO(fmjp2kLogger, "Output transfer syntax " << opt_oxferSyn.getXferName() << " can be written"); 426 | } else { 427 | OFLOG_FATAL(fmjp2kLogger, "No conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible!"); 428 | return 1; 429 | } 430 | 431 | OFLOG_INFO(fmjp2kLogger, "creating output file " << opt_ofname); 432 | 433 | fileformat.loadAllDataIntoMemory(); 434 | error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc, 435 | OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), EWM_updateMeta); 436 | 437 | if (error.bad()) 438 | { 439 | OFLOG_FATAL(fmjp2kLogger, error.text() << ": writing file: " << opt_ofname); 440 | return 1; 441 | } 442 | 443 | OFLOG_INFO(fmjp2kLogger, "conversion successful"); 444 | 445 | // deregister global codecs 446 | FMJPEG2KEncoderRegistration::cleanup(); 447 | 448 | return 0; 449 | } 450 | -------------------------------------------------------------------------------- /include/fmjpeg2k/djcodece.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2015, Ing-Long Eric Kuo 4 | * All rights reserved. See COPYRIGHT file for details. 5 | * 6 | * This software and supporting documentation were developed by 7 | * 8 | * 9 | * 10 | * Module: fmjpeg2k 11 | * 12 | * Author: Ing-Long Eric Kuo 13 | * 14 | * Purpose: codec classes for JPEG-2000 encoders. 15 | * 16 | */ 17 | 18 | #ifndef FMJPEG2K_DJCODEC_H 19 | #define FMJPEG2K_DJCODEC_H 20 | 21 | #include "dcmtk/config/osconfig.h" 22 | #include "dcmtk/dcmdata/dccodec.h" /* for class DcmCodec */ 23 | #include "dcmtk/dcmdata/dcofsetl.h" /* for struct DcmOffsetList */ 24 | #include "dcmtk/ofstd/ofstring.h" /* for class OFString */ 25 | #include "dldefine.h" 26 | 27 | class FMJPEG2KRepresentationParameter; 28 | class DJPEG2KCodecParameter; 29 | class DicomImage; 30 | 31 | /** abstract codec class for JPEG-2000 encoders. 32 | * This abstract class contains most of the application logic 33 | * needed for a dcmdata codec object that implements a JPEG-2000 encoder 34 | * This class only supports compression, it neither implements 35 | * decoding nor transcoding. 36 | */ 37 | class FMJPEG2K_EXPORT DJPEG2KEncoderBase : public DcmCodec 38 | { 39 | public: 40 | 41 | /// default constructor 42 | DJPEG2KEncoderBase(); 43 | 44 | /// destructor 45 | virtual ~DJPEG2KEncoderBase(); 46 | 47 | /** decompresses the given pixel sequence and 48 | * stores the result in the given uncompressedPixelData element. 49 | * @param fromRepParam current representation parameter of compressed data, may be NULL 50 | * @param pixSeq compressed pixel sequence 51 | * @param uncompressedPixelData uncompressed pixel data stored in this element 52 | * @param cp codec parameters for this codec 53 | * @param objStack stack pointing to the location of the pixel data 54 | * element in the current dataset. 55 | * @return EC_Normal if successful, an error code otherwise. 56 | */ 57 | virtual OFCondition decode( 58 | const DcmRepresentationParameter * fromRepParam, 59 | DcmPixelSequence * pixSeq, 60 | DcmPolymorphOBOW& uncompressedPixelData, 61 | const DcmCodecParameter * cp, 62 | const DcmStack& objStack) const; 63 | 64 | /** decompresses the given pixel sequence and 65 | * stores the result in the given uncompressedPixelData element. 66 | * @param fromRepParam current representation parameter of compressed data, may be NULL 67 | * @param pixSeq compressed pixel sequence 68 | * @param uncompressedPixelData uncompressed pixel data stored in this element 69 | * @param cp codec parameters for this codec 70 | * @param objStack stack pointing to the location of the pixel data 71 | * element in the current dataset. 72 | * @param removeOldRep boolean flag that should be set to false before this method call 73 | * and will be set to true if the codec modifies the DICOM dataset such 74 | * that the pixel data of the original representation may not be usable 75 | * anymore. 76 | * @return EC_Normal if successful, an error code otherwise. 77 | */ 78 | virtual OFCondition decode( 79 | const DcmRepresentationParameter * fromRepParam, 80 | DcmPixelSequence * pixSeq, 81 | DcmPolymorphOBOW& uncompressedPixelData, 82 | const DcmCodecParameter * cp, 83 | const DcmStack& objStack, 84 | OFBool& removeOldRep) const; 85 | 86 | /** decompresses a single frame from the given pixel sequence and 87 | * stores the result in the given buffer. 88 | * @param fromParam representation parameter of current compressed 89 | * representation, may be NULL. 90 | * @param fromPixSeq compressed pixel sequence 91 | * @param cp codec parameters for this codec 92 | * @param dataset pointer to dataset in which pixel data element is contained 93 | * @param frameNo number of frame, starting with 0 for the first frame 94 | * @param startFragment index of the compressed fragment that contains 95 | * all or the first part of the compressed bitstream for the given frameNo. 96 | * Upon successful return this parameter is updated to contain the index 97 | * of the first compressed fragment of the next frame. 98 | * When unknown, zero should be passed. In this case the decompression 99 | * algorithm will try to determine the index by itself, which will always 100 | * work if frames are decompressed in increasing order from first to last, 101 | * but may fail if frames are decompressed in random order, multiple fragments 102 | * per frame and multiple frames are present in the dataset, and the offset 103 | * table is empty. 104 | * @param buffer pointer to buffer where frame is to be stored 105 | * @param bufSize size of buffer in bytes 106 | * @param decompressedColorModel upon successful return, the color model 107 | * of the decompressed image (which may be different from the one used 108 | * in the compressed images) is returned in this parameter. 109 | * @return EC_Normal if successful, an error code otherwise. 110 | */ 111 | virtual OFCondition decodeFrame( 112 | const DcmRepresentationParameter * fromParam, 113 | DcmPixelSequence * fromPixSeq, 114 | const DcmCodecParameter * cp, 115 | DcmItem *dataset, 116 | Uint32 frameNo, 117 | Uint32& startFragment, 118 | void *buffer, 119 | Uint32 bufSize, 120 | OFString& decompressedColorModel) const; 121 | 122 | /** compresses the given uncompressed DICOM image and stores 123 | * the result in the given pixSeq element. 124 | * @param pixelData pointer to the uncompressed image data in OW format 125 | * and local byte order 126 | * @param length of the pixel data field in bytes 127 | * @param toRepParam representation parameter describing the desired 128 | * compressed representation (e.g. JPEG quality) 129 | * @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 130 | * allocated on heap) returned in this parameter upon success. 131 | * @param cp codec parameters for this codec 132 | * @param objStack stack pointing to the location of the pixel data 133 | * element in the current dataset. 134 | * @return EC_Normal if successful, an error code otherwise. 135 | */ 136 | virtual OFCondition encode( 137 | const Uint16 * pixelData, 138 | const Uint32 length, 139 | const DcmRepresentationParameter * toRepParam, 140 | DcmPixelSequence * & pixSeq, 141 | const DcmCodecParameter *cp, 142 | DcmStack & objStack) const; 143 | 144 | /** compresses the given uncompressed DICOM image and stores 145 | * the result in the given pixSeq element. 146 | * @param pixelData pointer to the uncompressed image data in OW format 147 | * and local byte order 148 | * @param length of the pixel data field in bytes 149 | * @param toRepParam representation parameter describing the desired 150 | * compressed representation (e.g. JPEG quality) 151 | * @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 152 | * allocated on heap) returned in this parameter upon success. 153 | * @param cp codec parameters for this codec 154 | * @param objStack stack pointing to the location of the pixel data 155 | * element in the current dataset. 156 | * @param removeOldRep boolean flag that should be set to false before this method call 157 | * and will be set to true if the codec modifies the DICOM dataset such 158 | * that the pixel data of the original representation may not be usable 159 | * anymore. 160 | * @return EC_Normal if successful, an error code otherwise. 161 | */ 162 | virtual OFCondition encode( 163 | const Uint16 * pixelData, 164 | const Uint32 length, 165 | const DcmRepresentationParameter * toRepParam, 166 | DcmPixelSequence * & pixSeq, 167 | const DcmCodecParameter *cp, 168 | DcmStack & objStack, 169 | OFBool& removeOldRep) const; 170 | 171 | /** transcodes (re-compresses) the given compressed DICOM image and stores 172 | * the result in the given toPixSeq element. 173 | * @param fromRepType current transfer syntax of the compressed image 174 | * @param fromRepParam current representation parameter of compressed data, may be NULL 175 | * @param fromPixSeq compressed pixel sequence 176 | * @param toRepParam representation parameter describing the desired 177 | * new compressed representation (e.g. JPEG quality) 178 | * @param toPixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 179 | * allocated on heap) returned in this parameter upon success. 180 | * @param cp codec parameters for this codec 181 | * @param objStack stack pointing to the location of the pixel data 182 | * element in the current dataset. 183 | * @return EC_Normal if successful, an error code otherwise. 184 | */ 185 | virtual OFCondition encode( 186 | const E_TransferSyntax fromRepType, 187 | const DcmRepresentationParameter * fromRepParam, 188 | DcmPixelSequence * fromPixSeq, 189 | const DcmRepresentationParameter * toRepParam, 190 | DcmPixelSequence * & toPixSeq, 191 | const DcmCodecParameter * cp, 192 | DcmStack & objStack) const; 193 | 194 | /** transcodes (re-compresses) the given compressed DICOM image and stores 195 | * the result in the given toPixSeq element. 196 | * @param fromRepType current transfer syntax of the compressed image 197 | * @param fromRepParam current representation parameter of compressed data, may be NULL 198 | * @param fromPixSeq compressed pixel sequence 199 | * @param toRepParam representation parameter describing the desired 200 | * new compressed representation (e.g. JPEG quality) 201 | * @param toPixSeq compressed pixel sequence (pointer to new DcmPixelSequence object 202 | * allocated on heap) returned in this parameter upon success. 203 | * @param cp codec parameters for this codec 204 | * @param objStack stack pointing to the location of the pixel data 205 | * element in the current dataset. 206 | * @param removeOldRep boolean flag that should be set to false before this method call 207 | * and will be set to true if the codec modifies the DICOM dataset such 208 | * that the pixel data of the original representation may not be usable 209 | * anymore. 210 | * @return EC_Normal if successful, an error code otherwise. 211 | */ 212 | virtual OFCondition encode( 213 | const E_TransferSyntax fromRepType, 214 | const DcmRepresentationParameter * fromRepParam, 215 | DcmPixelSequence * fromPixSeq, 216 | const DcmRepresentationParameter * toRepParam, 217 | DcmPixelSequence * & toPixSeq, 218 | const DcmCodecParameter * cp, 219 | DcmStack & objStack, 220 | OFBool& removeOldRep) const; 221 | 222 | /** checks if this codec is able to convert from the 223 | * given current transfer syntax to the given new 224 | * transfer syntax 225 | * @param oldRepType current transfer syntax 226 | * @param newRepType desired new transfer syntax 227 | * @return true if transformation is supported by this codec, false otherwise. 228 | */ 229 | virtual OFBool canChangeCoding( 230 | const E_TransferSyntax oldRepType, 231 | const E_TransferSyntax newRepType) const; 232 | 233 | /** determine color model of the decompressed image 234 | * @param fromParam representation parameter of current compressed 235 | * representation, may be NULL 236 | * @param fromPixSeq compressed pixel sequence 237 | * @param cp codec parameters for this codec 238 | * @param dataset pointer to dataset in which pixel data element is contained 239 | * @param dataset pointer to DICOM dataset in which this pixel data object 240 | * is located. Used to access photometric interpretation. 241 | * @param decompressedColorModel upon successful return, the color model 242 | * of the decompressed image (which may be different from the one used 243 | * in the compressed images) is returned in this parameter 244 | * @return EC_Normal if successful, an error code otherwise 245 | */ 246 | virtual OFCondition determineDecompressedColorModel( 247 | const DcmRepresentationParameter *fromParam, 248 | DcmPixelSequence *fromPixSeq, 249 | const DcmCodecParameter *cp, 250 | DcmItem *dataset, 251 | OFString &decompressedColorModel) const; 252 | 253 | private: 254 | 255 | /** returns the transfer syntax that this particular codec 256 | * is able to encode 257 | * @return supported transfer syntax 258 | */ 259 | virtual E_TransferSyntax supportedTransferSyntax() const = 0; 260 | 261 | /** lossless encoder that compresses the complete pixel cell 262 | * (very much like the RLE encoder in module dcmdata). 263 | * @param pixelData pointer to the uncompressed image data in OW format 264 | * and local byte order 265 | * @param length of the pixel data field in bytes 266 | * @param dataset pointer to dataset containing image pixel module 267 | * @param djrp representation parameter 268 | * @param pixSeq pixel sequence to write to 269 | * @param djcp codec parameter 270 | * @param compressionRatio compression ratio returned upon success 271 | * @return EC_Normal if successful, an error code otherwise. 272 | */ 273 | OFCondition losslessRawEncode( 274 | const Uint16 *pixelData, 275 | const Uint32 length, 276 | DcmItem *dataset, 277 | const FMJPEG2KRepresentationParameter *djrp, 278 | DcmPixelSequence * & pixSeq, 279 | const DJPEG2KCodecParameter *djcp, 280 | double& compressionRatio) const; 281 | 282 | /** lossless encoder that moves Overlays to (60xx,3000) and only 283 | * compresses the stored bits of the pixel cell. 284 | * @param pixelData pointer to the uncompressed image data in OW format 285 | * and local byte order 286 | * @param length of the pixel data field in bytes 287 | * @param dataset pointer to dataset containing image pixel module 288 | * @param djrp representation parameter 289 | * @param pixSeq pixel sequence to write to 290 | * @param djcp codec parameter 291 | * @param compressionRatio compression ratio returned upon success 292 | * @param nearLosslessDeviation maximum deviation for near-lossless encoding 293 | * @return EC_Normal if successful, an error code otherwise. 294 | */ 295 | OFCondition RenderedEncode( 296 | const Uint16 * pixelData, 297 | const Uint32 length, 298 | DcmItem *dataset, 299 | const FMJPEG2KRepresentationParameter *djrp, 300 | DcmPixelSequence * & pixSeq, 301 | const DJPEG2KCodecParameter *djcp, 302 | double& compressionRatio) const; 303 | 304 | /** for all overlay groups create (60xx,3000) Overlay Data. 305 | * @param dataset dataset to be modified 306 | * @param image DicomImage object for this dataset 307 | * @return EC_Normal if successful, an error code otherwise 308 | */ 309 | OFCondition adjustOverlays( 310 | DcmItem *dataset, 311 | DicomImage& image) const; 312 | 313 | /** create Lossy Image Compression and Lossy Image Compression Ratio. 314 | * @param dataset dataset to be modified 315 | * @param ratio image compression ratio > 1. This is the real effective ratio 316 | * between compressed and uncompressed image, 317 | * i. e. 30 means a 30:1 lossy compression. 318 | * @return EC_Normal if successful, an error code otherwise 319 | */ 320 | OFCondition updateLossyCompressionRatio( 321 | DcmItem *dataset, 322 | double ratio) const; 323 | 324 | /** create Derivation Description. 325 | * @param dataset dataset to be modified 326 | * @param djrp representation parameter passed to encode() 327 | * @param ratio image compression ratio > 1. This is the real effective ratio 328 | * between compressed and uncompressed image, 329 | * i. e. 30 means a 30:1 lossy compression. 330 | * @return EC_Normal if successful, an error code otherwise 331 | */ 332 | OFCondition updateDerivationDescription( 333 | DcmItem *dataset, 334 | const FMJPEG2KRepresentationParameter *djrp, 335 | double ratio) const; 336 | 337 | /** perform the lossless raw compression of a single frame 338 | * @param framePointer pointer to start of frame 339 | * @param bitsAllocated number of bits allocated per pixel 340 | * @param columns frame width 341 | * @param rows frame height 342 | * @param samplesPerPixel image samples per pixel 343 | * @param planarConfiguration image planar configuration 344 | * @param photometricInterpretation photometric interpretation of the DICOM dataset 345 | * @param pixelSequence object in which the compressed frame is stored 346 | * @param offsetList list of frame offsets updated in this parameter 347 | * @param compressedSize size of compressed frame returned in this parameter 348 | * @param djcp parameters for the codec 349 | * @return EC_Normal if successful, an error code otherwise 350 | */ 351 | OFCondition compressRawFrame( 352 | const Uint8 *framePointer, 353 | Uint16 bitsAllocated, 354 | Uint16 columns, 355 | Uint16 rows, 356 | Uint16 samplesPerPixel, 357 | Uint16 planarConfiguration, 358 | OFBool pixelRepresentation, 359 | const OFString& photometricInterpretation, 360 | DcmPixelSequence *pixelSequence, 361 | DcmOffsetList &offsetList, 362 | unsigned long &compressedSize, 363 | const DJPEG2KCodecParameter *djcp) const; 364 | 365 | /** perform the lossless compression of a single rendered frame 366 | * @param pixelSequence object in which the compressed frame is stored 367 | * @param dimage DicomImage instance used to process frame 368 | * @param photometricInterpretation photometric interpretation of the DICOM dataset 369 | * @param offsetList list of frame offsets updated in this parameter 370 | * @param compressedSize size of compressed frame returned in this parameter 371 | * @param djcp parameters for the codec 372 | * @param frame frame index 373 | * @param nearLosslessDeviation maximum deviation for near-lossless encoding 374 | * @return EC_Normal if successful, an error code otherwise 375 | */ 376 | OFCondition compressRenderedFrame( 377 | DcmPixelSequence *pixelSequence, 378 | DicomImage *dimage, 379 | const OFString& photometricInterpretation, 380 | DcmOffsetList &offsetList, 381 | unsigned long &compressedSize, 382 | const DJPEG2KCodecParameter *djcp, 383 | Uint32 frame, 384 | const FMJPEG2KRepresentationParameter *djrp) const; 385 | 386 | /** Convert an image from sample interleaved to uninterleaved. 387 | * @param target A buffer where the converted image will be stored 388 | * @param source The image buffer to be converted 389 | * @param components Color components used in the image 390 | * @param width The width of the image 391 | * @param height The height of the image 392 | * @param bitsAllocated The number of bits allocated in the image. 393 | * @return EC_Normal if succesful, an error code otherwise 394 | */ 395 | OFCondition convertToUninterleaved( 396 | Uint8 *target, 397 | const Uint8 *source, 398 | Uint16 components, 399 | Uint32 width, 400 | Uint32 height, 401 | Uint16 bitsAllocated) const; 402 | 403 | /** Convert an image from uninterleaved to sample interleaved. 404 | * @param target A buffer where the converted image will be stored 405 | * @param source The image buffer to be converted 406 | * @param components Color components used in the image 407 | * @param width The width of the image. 408 | * @param height The height of the image. 409 | * @param bitsAllocated The number of bits allocated in the image. 410 | * @return EC_Normal if succesful, an error code otherwise 411 | */ 412 | OFCondition convertToSampleInterleaved( 413 | Uint8 *target, 414 | const Uint8 *source, 415 | Uint16 components, 416 | Uint32 width, 417 | Uint32 height, 418 | Uint16 bitsAllocated) const; 419 | }; 420 | 421 | 422 | /** codec class for JPEG-2000 lossless only TS encoding 423 | */ 424 | class FMJPEG2K_EXPORT DJPEG2KLosslessEncoder : public DJPEG2KEncoderBase 425 | { 426 | /** returns the transfer syntax that this particular codec 427 | * is able to encode 428 | * @return supported transfer syntax 429 | */ 430 | virtual E_TransferSyntax supportedTransferSyntax() const; 431 | }; 432 | 433 | /** codec class for JPEG-2000 lossy and lossless TS encoding 434 | */ 435 | class FMJPEG2K_EXPORT DJPEG2KNearLosslessEncoder : public DJPEG2KEncoderBase 436 | { 437 | /** returns the transfer syntax that this particular codec 438 | * is able to encode 439 | * @return supported transfer syntax 440 | */ 441 | virtual E_TransferSyntax supportedTransferSyntax() const; 442 | }; 443 | 444 | #endif 445 | -------------------------------------------------------------------------------- /djcodecd.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * Module: fmjpeg2k 19 | * 20 | * Author: Ing-Long Eric Kuo 21 | * 22 | * Purpose: codec classes for JPEG-2000 decoders. 23 | * 24 | */ 25 | 26 | #include "dcmtk/config/osconfig.h" 27 | #include "fmjpeg2k/djcodecd.h" 28 | 29 | #include "dcmtk/ofstd/ofstream.h" /* for ofstream */ 30 | #include "dcmtk/ofstd/ofcast.h" /* for casts */ 31 | #include "dcmtk/ofstd/offile.h" /* for class OFFile */ 32 | #include "dcmtk/ofstd/ofstd.h" /* for class OFStandard */ 33 | #include "dcmtk/dcmdata/dcdatset.h" /* for class DcmDataset */ 34 | #include "dcmtk/dcmdata/dcdeftag.h" /* for tag constants */ 35 | #include "dcmtk/dcmdata/dcpixseq.h" /* for class DcmPixelSequence */ 36 | #include "dcmtk/dcmdata/dcpxitem.h" /* for class DcmPixelItem */ 37 | #include "dcmtk/dcmdata/dcvrpobw.h" /* for class DcmPolymorphOBOW */ 38 | #include "dcmtk/dcmdata/dcswap.h" /* for swapIfNecessary() */ 39 | #include "dcmtk/dcmdata/dcuid.h" /* for dcmGenerateUniqueIdentifer()*/ 40 | #include "fmjpeg2k/djcparam.h" /* for class DJP2KCodecParameter */ 41 | #include "fmjpeg2k/djerror.h" /* for private class DJLSError */ 42 | 43 | // JPEG-2000 library (OpenJPEG) includes 44 | #include "openjpeg.h" 45 | 46 | #include "fmjpeg2k/memory_file.h" 47 | 48 | 49 | 50 | 51 | 52 | DJPEG2KDecoderBase::DJPEG2KDecoderBase() 53 | : DcmCodec() 54 | { 55 | } 56 | 57 | 58 | DJPEG2KDecoderBase::~DJPEG2KDecoderBase() 59 | { 60 | } 61 | 62 | 63 | OFBool DJPEG2KDecoderBase::canChangeCoding( 64 | const E_TransferSyntax oldRepType, 65 | const E_TransferSyntax newRepType) const 66 | { 67 | // this codec only handles conversion from JPEG-2000 to uncompressed. 68 | 69 | DcmXfer newRep(newRepType); 70 | if (newRep.isNotEncapsulated() && 71 | ((oldRepType == EXS_JPEG2000LosslessOnly)||(oldRepType == EXS_JPEG2000)|| 72 | (oldRepType == EXS_JPEG2000MulticomponentLosslessOnly)||(oldRepType == EXS_JPEG2000Multicomponent))) 73 | return OFTrue; 74 | 75 | return OFFalse; 76 | } 77 | 78 | 79 | OFCondition DJPEG2KDecoderBase::decode( 80 | const DcmRepresentationParameter * /* fromRepParam */, 81 | DcmPixelSequence * pixSeq, 82 | DcmPolymorphOBOW& uncompressedPixelData, 83 | const DcmCodecParameter * cp, 84 | const DcmStack& objStack) const 85 | { 86 | // retrieve pointer to dataset from parameter stack 87 | DcmStack localStack(objStack); 88 | (void)localStack.pop(); // pop pixel data element from stack 89 | DcmObject *dobject = localStack.pop(); // this is the item in which the pixel data is located 90 | if ((!dobject)||((dobject->ident()!= EVR_dataset) && (dobject->ident()!= EVR_item))) return EC_InvalidTag; 91 | DcmItem *dataset = OFstatic_cast(DcmItem *, dobject); 92 | OFBool numberOfFramesPresent = OFFalse; 93 | 94 | // determine properties of uncompressed dataset 95 | Uint16 imageSamplesPerPixel = 0; 96 | if (dataset->findAndGetUint16(DCM_SamplesPerPixel, imageSamplesPerPixel).bad()) return EC_TagNotFound; 97 | // we only handle one or three samples per pixel 98 | if ((imageSamplesPerPixel != 3) && (imageSamplesPerPixel != 1)) return EC_InvalidTag; 99 | 100 | Uint16 imageRows = 0; 101 | if (dataset->findAndGetUint16(DCM_Rows, imageRows).bad()) return EC_TagNotFound; 102 | if (imageRows < 1) return EC_InvalidTag; 103 | 104 | Uint16 imageColumns = 0; 105 | if (dataset->findAndGetUint16(DCM_Columns, imageColumns).bad()) return EC_TagNotFound; 106 | if (imageColumns < 1) return EC_InvalidTag; 107 | 108 | // number of frames is an optional attribute - we don't mind if it isn't present. 109 | Sint32 imageFrames = 0; 110 | if (dataset->findAndGetSint32(DCM_NumberOfFrames, imageFrames).good()) numberOfFramesPresent = OFTrue; 111 | 112 | if (imageFrames >= OFstatic_cast(Sint32, pixSeq->card())) 113 | imageFrames = pixSeq->card() - 1; // limit number of frames to number of pixel items - 1 114 | if (imageFrames < 1) 115 | imageFrames = 1; // default in case the number of frames attribute is absent or contains garbage 116 | 117 | Uint16 imageBitsStored = 0; 118 | if (dataset->findAndGetUint16(DCM_BitsStored, imageBitsStored).bad()) return EC_TagNotFound; 119 | 120 | Uint16 imageBitsAllocated = 0; 121 | if (dataset->findAndGetUint16(DCM_BitsAllocated, imageBitsAllocated).bad()) return EC_TagNotFound; 122 | 123 | Uint16 imageHighBit = 0; 124 | if (dataset->findAndGetUint16(DCM_HighBit, imageHighBit).bad()) return EC_TagNotFound; 125 | 126 | //we only support up to 16 bits per sample 127 | if ((imageBitsStored < 1) || (imageBitsStored > 16)) return EC_J2KUnsupportedBitDepth; 128 | 129 | // determine the number of bytes per sample (bits allocated) for the de-compressed object. 130 | Uint16 bytesPerSample = 1; 131 | if (imageBitsStored > 8) bytesPerSample = 2; 132 | else if (imageBitsAllocated > 8) bytesPerSample = 2; 133 | 134 | // compute size of uncompressed frame, in bytes 135 | Uint32 frameSize = bytesPerSample * imageRows * imageColumns * imageSamplesPerPixel; 136 | 137 | // compute size of pixel data attribute, in bytes 138 | Uint32 totalSize = frameSize * imageFrames; 139 | if (totalSize & 1) totalSize++; // align on 16-bit word boundary 140 | 141 | // assume we can cast the codec parameter to what we need 142 | const DJPEG2KCodecParameter *djcp = OFreinterpret_cast(const DJPEG2KCodecParameter *, cp); 143 | 144 | // determine planar configuration for uncompressed data 145 | OFString imageSopClass; 146 | OFString imagePhotometricInterpretation; 147 | dataset->findAndGetOFString(DCM_SOPClassUID, imageSopClass); 148 | dataset->findAndGetOFString(DCM_PhotometricInterpretation, imagePhotometricInterpretation); 149 | 150 | // allocate space for uncompressed pixel data element 151 | Uint16 *pixeldata16 = NULL; 152 | OFCondition result = uncompressedPixelData.createUint16Array(totalSize/sizeof(Uint16), pixeldata16); 153 | if (result.bad()) return result; 154 | 155 | Uint8 *pixeldata8 = OFreinterpret_cast(Uint8 *, pixeldata16); 156 | Sint32 currentFrame = 0; 157 | Uint32 currentItem = 1; // item 0 contains the offset table 158 | OFBool done = OFFalse; 159 | 160 | while (result.good() && !done) 161 | { 162 | FMJPEG2K_DEBUG("JPEG-2000 decoder processes frame " << (currentFrame+1)); 163 | 164 | result = decodeFrame(pixSeq, djcp, dataset, currentFrame, currentItem, pixeldata8, frameSize, 165 | imageFrames, imageColumns, imageRows, imageSamplesPerPixel, bytesPerSample); 166 | 167 | if (result.good()) 168 | { 169 | // increment frame number, check if we're finished 170 | if (++currentFrame == imageFrames) done = OFTrue; 171 | pixeldata8 += frameSize; 172 | } 173 | } 174 | 175 | // Number of Frames might have changed in case the previous value was wrong 176 | if (result.good() && (numberOfFramesPresent || (imageFrames > 1))) 177 | { 178 | char numBuf[20]; 179 | sprintf(numBuf, "%ld", OFstatic_cast(long, imageFrames)); 180 | result = ((DcmItem *)dataset)->putAndInsertString(DCM_NumberOfFrames, numBuf); 181 | } 182 | 183 | if (result.good()) 184 | { 185 | // the following operations do not affect the Image Pixel Module 186 | // but other modules such as SOP Common. We only perform these 187 | // changes if we're on the main level of the dataset, 188 | // which should always identify itself as dataset, not as item. 189 | if ((dataset->ident() == EVR_dataset) && (djcp->getUIDCreation() == EJ2KUC_always)) 190 | { 191 | // create new SOP instance UID 192 | result = DcmCodec::newInstance((DcmItem *)dataset, NULL, NULL, NULL); 193 | } 194 | } 195 | 196 | return result; 197 | } 198 | 199 | 200 | OFCondition DJPEG2KDecoderBase::decode( 201 | const DcmRepresentationParameter * fromRepParam, 202 | DcmPixelSequence * pixSeq, 203 | DcmPolymorphOBOW& uncompressedPixelData, 204 | const DcmCodecParameter * cp, 205 | const DcmStack& objStack, 206 | OFBool& removeOldRep) const 207 | { 208 | // removeOldRep is left as it is, pixel data in original DICOM dataset is not modified 209 | return decode(fromRepParam, pixSeq, uncompressedPixelData, cp, objStack); 210 | } 211 | 212 | 213 | OFCondition DJPEG2KDecoderBase::decodeFrame( 214 | const DcmRepresentationParameter * /* fromParam */, 215 | DcmPixelSequence *fromPixSeq, 216 | const DcmCodecParameter *cp, 217 | DcmItem *dataset, 218 | Uint32 frameNo, 219 | Uint32& currentItem, 220 | void * buffer, 221 | Uint32 bufSize, 222 | OFString& decompressedColorModel) const 223 | { 224 | OFCondition result = EC_Normal; 225 | 226 | // assume we can cast the codec parameter to what we need 227 | const DJPEG2KCodecParameter *djcp = OFreinterpret_cast(const DJPEG2KCodecParameter *, cp); 228 | 229 | // determine properties of uncompressed dataset 230 | Uint16 imageSamplesPerPixel = 0; 231 | if (dataset->findAndGetUint16(DCM_SamplesPerPixel, imageSamplesPerPixel).bad()) return EC_TagNotFound; 232 | // we only handle one or three samples per pixel 233 | if ((imageSamplesPerPixel != 3) && (imageSamplesPerPixel != 1)) return EC_InvalidTag; 234 | 235 | Uint16 imageRows = 0; 236 | if (dataset->findAndGetUint16(DCM_Rows, imageRows).bad()) return EC_TagNotFound; 237 | if (imageRows < 1) return EC_InvalidTag; 238 | 239 | Uint16 imageColumns = 0; 240 | if (dataset->findAndGetUint16(DCM_Columns, imageColumns).bad()) return EC_TagNotFound; 241 | if (imageColumns < 1) return EC_InvalidTag; 242 | 243 | Uint16 imageBitsStored = 0; 244 | if (dataset->findAndGetUint16(DCM_BitsStored, imageBitsStored).bad()) return EC_TagNotFound; 245 | 246 | Uint16 imageBitsAllocated = 0; 247 | if (dataset->findAndGetUint16(DCM_BitsAllocated, imageBitsAllocated).bad()) return EC_TagNotFound; 248 | 249 | //we only support up to 16 bits per sample 250 | if ((imageBitsStored < 1) || (imageBitsStored > 16)) return EC_J2KUnsupportedBitDepth; 251 | 252 | // determine the number of bytes per sample (bits allocated) for the de-compressed object. 253 | Uint16 bytesPerSample = 1; 254 | if (imageBitsStored > 8) bytesPerSample = 2; 255 | else if (imageBitsAllocated > 8) bytesPerSample = 2; 256 | 257 | // number of frames is an optional attribute - we don't mind if it isn't present. 258 | Sint32 imageFrames = 0; 259 | dataset->findAndGetSint32(DCM_NumberOfFrames, imageFrames).good(); 260 | 261 | if (imageFrames >= OFstatic_cast(Sint32, fromPixSeq->card())) 262 | imageFrames = fromPixSeq->card() - 1; // limit number of frames to number of pixel items - 1 263 | if (imageFrames < 1) 264 | imageFrames = 1; // default in case the number of frames attribute is absent or contains garbage 265 | 266 | // if the user has provided this information, we trust him. 267 | // If the user has passed a zero, try to find out ourselves. 268 | if (currentItem == 0) 269 | { 270 | result = determineStartFragment(frameNo, imageFrames, fromPixSeq, currentItem); 271 | } 272 | 273 | if (result.good()) 274 | { 275 | // We got all the data we need from the dataset, let's start decoding 276 | FMJPEG2K_DEBUG("Starting to decode frame " << frameNo << " with fragment " << currentItem); 277 | result = decodeFrame(fromPixSeq, djcp, dataset, frameNo, currentItem, buffer, bufSize, 278 | imageFrames, imageColumns, imageRows, imageSamplesPerPixel, bytesPerSample); 279 | } 280 | 281 | if (result.good()) 282 | { 283 | // retrieve color model from given dataset 284 | result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, decompressedColorModel); 285 | } 286 | 287 | return result; 288 | } 289 | 290 | OFCondition copyUint32ToUint8( 291 | opj_image_t * image, 292 | Uint8 *imageFrame, 293 | Uint16 columns, 294 | Uint16 rows); 295 | 296 | OFCondition copyUint32ToUint16( 297 | opj_image_t * image, 298 | Uint16 *imageFrame, 299 | Uint16 columns, 300 | Uint16 rows); 301 | 302 | OFCondition copyRGBUint8ToRGBUint8( 303 | opj_image_t * image, 304 | Uint8 *imageFrame, 305 | Uint16 columns, 306 | Uint16 rows); 307 | 308 | OFCondition copyRGBUint8ToRGBUint8Planar( 309 | opj_image_t * image, 310 | Uint8 *imageFrame, 311 | Uint16 columns, 312 | Uint16 rows); 313 | 314 | OFCondition DJPEG2KDecoderBase::decodeFrame( 315 | DcmPixelSequence * fromPixSeq, 316 | const DJPEG2KCodecParameter *cp, 317 | DcmItem *dataset, 318 | Uint32 frameNo, 319 | Uint32& currentItem, 320 | void * buffer, 321 | Uint32 bufSize, 322 | Sint32 imageFrames, 323 | Uint16 imageColumns, 324 | Uint16 imageRows, 325 | Uint16 imageSamplesPerPixel, 326 | Uint16 bytesPerSample) 327 | { 328 | DcmPixelItem *pixItem = NULL; 329 | Uint8 * jlsData = NULL; 330 | Uint8 * jlsFragmentData = NULL; 331 | Uint32 fragmentLength = 0; 332 | size_t compressedSize = 0; 333 | Uint32 fragmentsForThisFrame = 0; 334 | OFCondition result = EC_Normal; 335 | OFBool ignoreOffsetTable = cp->ignoreOffsetTable(); 336 | 337 | // compute the number of JPEG-LS fragments we need in order to decode the next frame 338 | fragmentsForThisFrame = computeNumberOfFragments(imageFrames, frameNo, currentItem, ignoreOffsetTable, fromPixSeq); 339 | if (fragmentsForThisFrame == 0) result = EC_J2KCannotComputeNumberOfFragments; 340 | 341 | // determine planar configuration for uncompressed data 342 | OFString imageSopClass; 343 | OFString imagePhotometricInterpretation; 344 | dataset->findAndGetOFString(DCM_SOPClassUID, imageSopClass); 345 | dataset->findAndGetOFString(DCM_PhotometricInterpretation, imagePhotometricInterpretation); 346 | Uint16 imagePlanarConfiguration = 0; // 0 is color-by-pixel, 1 is color-by-plane 347 | 348 | if (imageSamplesPerPixel > 1) 349 | { 350 | switch (cp->getPlanarConfiguration()) 351 | { 352 | case EJ2KPC_restore: 353 | // get planar configuration from dataset 354 | imagePlanarConfiguration = 2; // invalid value 355 | dataset->findAndGetUint16(DCM_PlanarConfiguration, imagePlanarConfiguration); 356 | // determine auto default if not found or invalid 357 | if (imagePlanarConfiguration > 1) 358 | imagePlanarConfiguration = determinePlanarConfiguration(imageSopClass, imagePhotometricInterpretation); 359 | break; 360 | case EJ2KPC_auto: 361 | imagePlanarConfiguration = determinePlanarConfiguration(imageSopClass, imagePhotometricInterpretation); 362 | break; 363 | case EJ2KPC_colorByPixel: 364 | imagePlanarConfiguration = 0; 365 | break; 366 | case EJ2KPC_colorByPlane: 367 | imagePlanarConfiguration = 1; 368 | break; 369 | } 370 | } 371 | 372 | // get the size of all the fragments 373 | if (result.good()) 374 | { 375 | // Don't modify the original values for now 376 | Uint32 fragmentsForThisFrame2 = fragmentsForThisFrame; 377 | Uint32 currentItem2 = currentItem; 378 | 379 | while (result.good() && fragmentsForThisFrame2--) 380 | { 381 | result = fromPixSeq->getItem(pixItem, currentItem2++); 382 | if (result.good() && pixItem) 383 | { 384 | fragmentLength = pixItem->getLength(); 385 | if (result.good()) 386 | compressedSize += fragmentLength; 387 | } 388 | } /* while */ 389 | } 390 | 391 | // get the compressed data 392 | if (result.good()) 393 | { 394 | Uint32 offset = 0; 395 | jlsData = new Uint8[compressedSize]; 396 | 397 | while (result.good() && fragmentsForThisFrame--) 398 | { 399 | result = fromPixSeq->getItem(pixItem, currentItem++); 400 | if (result.good() && pixItem) 401 | { 402 | fragmentLength = pixItem->getLength(); 403 | result = pixItem->getUint8Array(jlsFragmentData); 404 | if (result.good() && jlsFragmentData) 405 | { 406 | memcpy(&jlsData[offset], jlsFragmentData, fragmentLength); 407 | offset += fragmentLength; 408 | } 409 | } 410 | } /* while */ 411 | } 412 | 413 | if (result.good()) 414 | { 415 | // see if the last byte is a padding, otherwise, it should be 0xd9 416 | if(jlsData[compressedSize - 1] == 0) 417 | compressedSize--; 418 | 419 | DecodeData mysrc((unsigned char*)jlsData, compressedSize); 420 | 421 | // start of open jpeg stuff 422 | opj_dparameters_t parameters; 423 | opj_codec_t* l_codec = NULL; 424 | opj_stream_t *l_stream = NULL; 425 | opj_image_t *image = NULL; 426 | 427 | l_stream = opj_stream_create_memory_stream(&mysrc, OPJ_J2K_STREAM_CHUNK_SIZE, true); 428 | 429 | // figure out codec 430 | #define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a" 431 | #define JP2_MAGIC "\x0d\x0a\x87\x0a" 432 | /* position 45: "\xff\x52" */ 433 | #define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51" 434 | 435 | OPJ_CODEC_FORMAT format = OPJ_CODEC_UNKNOWN; 436 | if(memcmp(jlsData, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(jlsData, JP2_MAGIC, 4) == 0) 437 | format = OPJ_CODEC_JP2; 438 | else if (memcmp(jlsData, J2K_CODESTREAM_MAGIC, 4) == 0) 439 | format = OPJ_CODEC_J2K; 440 | else 441 | format = OPJ_CODEC_J2K; 442 | 443 | l_codec = opj_create_decompress(format); 444 | 445 | opj_set_info_handler(l_codec, msg_callback, NULL); 446 | opj_set_warning_handler(l_codec, msg_callback, NULL); 447 | opj_set_error_handler(l_codec, msg_callback, NULL); 448 | 449 | opj_set_default_decoder_parameters(¶meters); 450 | 451 | if(!opj_setup_decoder(l_codec, ¶meters)) 452 | { 453 | opj_stream_destroy(l_stream); l_stream = NULL; 454 | opj_destroy_codec(l_codec); l_codec = NULL; 455 | result = EC_CorruptedData; 456 | } 457 | 458 | if(result.good() && !opj_read_header(l_stream, l_codec, &image)) 459 | { 460 | opj_stream_destroy(l_stream); l_stream = NULL; 461 | opj_destroy_codec(l_codec); l_codec = NULL; 462 | opj_image_destroy(image); image = NULL; 463 | result = EC_CorruptedData; 464 | } 465 | 466 | if (result.good()) 467 | { 468 | if (image->x1 != imageColumns) result = EC_J2KImageDataMismatch; 469 | else if (image->y1 != imageRows) result = EC_J2KImageDataMismatch; 470 | else if (image->numcomps != imageSamplesPerPixel) result = EC_J2KImageDataMismatch; 471 | //else if ((bytesPerSample == 1) && (image->bitspersample > 8)) result = EC_J2KImageDataMismatch; 472 | //else if ((bytesPerSample == 2) && (image->bitspersample <= 8)) result = EC_J2KImageDataMismatch; 473 | } 474 | 475 | if (!result.good()) 476 | { 477 | delete[] jlsData; 478 | } 479 | else 480 | { 481 | if (!(opj_decode(l_codec, l_stream, image) && opj_end_decompress(l_codec, l_stream))) { 482 | opj_stream_destroy(l_stream); l_stream = NULL; 483 | opj_destroy_codec(l_codec); l_codec = NULL; 484 | opj_image_destroy(image); image = NULL; 485 | result = EC_CorruptedData; 486 | } 487 | 488 | if(result.good()) 489 | { 490 | // copy the image depending on planer configuration and bits 491 | if(image->numcomps == 1) // Greyscale 492 | { 493 | if(image->comps[0].prec <= 8) 494 | copyUint32ToUint8(image, OFreinterpret_cast(Uint8*, buffer), imageColumns, imageRows); 495 | if(image->comps[0].prec > 8) 496 | copyUint32ToUint16(image, OFreinterpret_cast(Uint16*, buffer), imageColumns, imageRows); 497 | } 498 | else if(image->numcomps == 3) 499 | { 500 | if(imagePlanarConfiguration == 0) 501 | { 502 | copyRGBUint8ToRGBUint8(image, OFreinterpret_cast(Uint8*, buffer), imageColumns, imageRows); 503 | } 504 | else if(imagePlanarConfiguration == 1) 505 | { 506 | copyRGBUint8ToRGBUint8Planar(image, OFreinterpret_cast(Uint8*, buffer), imageColumns, imageRows); 507 | } 508 | } 509 | } 510 | 511 | opj_stream_destroy(l_stream); l_stream = NULL; 512 | opj_destroy_codec(l_codec); l_codec = NULL; 513 | opj_image_destroy(image); image = NULL; 514 | delete[] jlsData; 515 | 516 | if (result.good()) 517 | { 518 | // decompression is complete, finally adjust byte order if necessary 519 | if (bytesPerSample == 1) // we're writing bytes into words 520 | { 521 | result = swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, buffer, 522 | bufSize, sizeof(Uint16)); 523 | } 524 | } 525 | } 526 | } 527 | 528 | return result; 529 | } 530 | 531 | 532 | OFCondition DJPEG2KDecoderBase::encode( 533 | const Uint16 * /* pixelData */, 534 | const Uint32 /* length */, 535 | const DcmRepresentationParameter * /* toRepParam */, 536 | DcmPixelSequence * & /* pixSeq */, 537 | const DcmCodecParameter * /* cp */, 538 | DcmStack & /* objStack */) const 539 | { 540 | // we are a decoder only 541 | return EC_IllegalCall; 542 | } 543 | 544 | 545 | OFCondition DJPEG2KDecoderBase::encode( 546 | const Uint16 * pixelData, 547 | const Uint32 length, 548 | const DcmRepresentationParameter * toRepParam, 549 | DcmPixelSequence * & pixSeq, 550 | const DcmCodecParameter *cp, 551 | DcmStack & objStack, 552 | OFBool& removeOldRep) const 553 | { 554 | return EC_IllegalCall; 555 | } 556 | 557 | 558 | OFCondition DJPEG2KDecoderBase::encode( 559 | const E_TransferSyntax /* fromRepType */, 560 | const DcmRepresentationParameter * /* fromRepParam */, 561 | DcmPixelSequence * /* fromPixSeq */, 562 | const DcmRepresentationParameter * /* toRepParam */, 563 | DcmPixelSequence * & /* toPixSeq */, 564 | const DcmCodecParameter * /* cp */, 565 | DcmStack & /* objStack */) const 566 | { 567 | // we don't support re-coding for now. 568 | return EC_IllegalCall; 569 | } 570 | 571 | 572 | OFCondition DJPEG2KDecoderBase::encode( 573 | const E_TransferSyntax fromRepType, 574 | const DcmRepresentationParameter * fromRepParam, 575 | DcmPixelSequence * fromPixSeq, 576 | const DcmRepresentationParameter * toRepParam, 577 | DcmPixelSequence * & toPixSeq, 578 | const DcmCodecParameter * cp, 579 | DcmStack & objStack, 580 | OFBool& removeOldRep) const 581 | { 582 | return EC_IllegalCall; 583 | } 584 | 585 | 586 | OFCondition DJPEG2KDecoderBase::determineDecompressedColorModel( 587 | const DcmRepresentationParameter * /* fromParam */, 588 | DcmPixelSequence * /* fromPixSeq */, 589 | const DcmCodecParameter * /* cp */, 590 | DcmItem * dataset, 591 | OFString & decompressedColorModel) const 592 | { 593 | OFCondition result = EC_IllegalParameter; 594 | if (dataset != NULL) 595 | { 596 | // retrieve color model from given dataset 597 | result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, decompressedColorModel); 598 | } 599 | return result; 600 | } 601 | 602 | 603 | Uint16 DJPEG2KDecoderBase::determinePlanarConfiguration( 604 | const OFString& sopClassUID, 605 | const OFString& photometricInterpretation) 606 | { 607 | // Hardcopy Color Image always requires color-by-plane 608 | if (sopClassUID == UID_RETIRED_HardcopyColorImageStorage) return 1; 609 | 610 | // The 1996 Ultrasound Image IODs require color-by-plane if color model is YBR_FULL. 611 | if (photometricInterpretation == "YBR_FULL") 612 | { 613 | if ((sopClassUID == UID_UltrasoundMultiframeImageStorage) 614 | ||(sopClassUID == UID_UltrasoundImageStorage)) return 1; 615 | } 616 | 617 | // default for all other cases 618 | return 0; 619 | } 620 | 621 | Uint32 DJPEG2KDecoderBase::computeNumberOfFragments( 622 | Sint32 numberOfFrames, 623 | Uint32 currentFrame, 624 | Uint32 startItem, 625 | OFBool ignoreOffsetTable, 626 | DcmPixelSequence * pixSeq) 627 | { 628 | 629 | unsigned long numItems = pixSeq->card(); 630 | DcmPixelItem *pixItem = NULL; 631 | 632 | // We first check the simple cases, that is, a single-frame image, 633 | // the last frame of a multi-frame image and the standard case where we do have 634 | // a single fragment per frame. 635 | if ((numberOfFrames <= 1) || (currentFrame + 1 == OFstatic_cast(Uint32, numberOfFrames))) 636 | { 637 | // single-frame image or last frame. All remaining fragments belong to this frame 638 | return (numItems - startItem); 639 | } 640 | if (OFstatic_cast(Uint32, numberOfFrames + 1) == numItems) 641 | { 642 | // multi-frame image with one fragment per frame 643 | return 1; 644 | } 645 | 646 | OFCondition result = EC_Normal; 647 | if (! ignoreOffsetTable) 648 | { 649 | // We do have a multi-frame image with multiple fragments per frame, and we are 650 | // not working on the last frame. Let's check the offset table if present. 651 | result = pixSeq->getItem(pixItem, 0); 652 | if (result.good() && pixItem) 653 | { 654 | Uint32 offsetTableLength = pixItem->getLength(); 655 | if (offsetTableLength == (OFstatic_cast(Uint32, numberOfFrames) * 4)) 656 | { 657 | // offset table is non-empty and contains one entry per frame 658 | Uint8 *offsetData = NULL; 659 | result = pixItem->getUint8Array(offsetData); 660 | if (result.good() && offsetData) 661 | { 662 | // now we can access the offset table 663 | Uint32 *offsetData32 = OFreinterpret_cast(Uint32 *, offsetData); 664 | 665 | // extract the offset for the NEXT frame. This offset is guaranteed to exist 666 | // because the "last frame/single frame" case is handled above. 667 | Uint32 offset = offsetData32[currentFrame+1]; 668 | 669 | // convert to local endian byte order (always little endian in file) 670 | swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, &offset, sizeof(Uint32), sizeof(Uint32)); 671 | 672 | // determine index of start fragment for next frame 673 | Uint32 byteCount = 0; 674 | Uint32 fragmentIndex = 1; 675 | while ((byteCount < offset) && (fragmentIndex < numItems)) 676 | { 677 | pixItem = NULL; 678 | result = pixSeq->getItem(pixItem, fragmentIndex++); 679 | if (result.good() && pixItem) 680 | { 681 | byteCount += pixItem->getLength() + 8; // add 8 bytes for item tag and length 682 | if ((byteCount == offset) && (fragmentIndex > startItem)) 683 | { 684 | // bingo, we have found the offset for the next frame 685 | return fragmentIndex - startItem; 686 | } 687 | } 688 | else break; /* something went wrong, break out of while loop */ 689 | } /* while */ 690 | } 691 | } 692 | } 693 | } 694 | 695 | // So we have a multi-frame image with multiple fragments per frame and the 696 | // offset table is empty or wrong. Our last chance is to peek into the JPEG-2000 697 | // bistream and identify the start of the next frame. 698 | Uint32 nextItem = startItem; 699 | Uint8 *fragmentData = NULL; 700 | while (++nextItem < numItems) 701 | { 702 | pixItem = NULL; 703 | result = pixSeq->getItem(pixItem, nextItem); 704 | if (result.good() && pixItem) 705 | { 706 | fragmentData = NULL; 707 | result = pixItem->getUint8Array(fragmentData); 708 | if (result.good() && fragmentData && (pixItem->getLength() > 3)) 709 | { 710 | if (isJPEGLSStartOfImage(fragmentData)) 711 | { 712 | // found a JPEG-2000 SOI marker. Assume that this is the start of the next frame. 713 | return (nextItem - startItem); 714 | } 715 | } 716 | else break; /* something went wrong, break out of while loop */ 717 | } 718 | else break; /* something went wrong, break out of while loop */ 719 | } 720 | 721 | // We're bust. No way to determine the number of fragments per frame. 722 | return 0; 723 | } 724 | 725 | 726 | OFBool DJPEG2KDecoderBase::isJPEGLSStartOfImage(Uint8 *fragmentData) 727 | { 728 | // A valid JPEG-2000 bitstream will always start with an SOI marker FFD8, followed 729 | // by either an SOF55 (FFF7), COM (FFFE) or APPn (FFE0-FFEF) marker. 730 | if ((*fragmentData++) != 0xFF) return OFFalse; 731 | if ((*fragmentData++) != 0xD8) return OFFalse; 732 | if ((*fragmentData++) != 0xFF) return OFFalse; 733 | if ((*fragmentData == 0xF7)||(*fragmentData == 0xFE)||((*fragmentData & 0xF0) == 0xE0)) 734 | { 735 | return OFTrue; 736 | } 737 | return OFFalse; 738 | } 739 | 740 | 741 | OFCondition DJPEG2KDecoderBase::createPlanarConfiguration1Byte( 742 | Uint8 *imageFrame, 743 | Uint16 columns, 744 | Uint16 rows) 745 | { 746 | if (imageFrame == NULL) return EC_IllegalCall; 747 | 748 | unsigned long numPixels = columns * rows; 749 | if (numPixels == 0) return EC_IllegalCall; 750 | 751 | Uint8 *buf = new Uint8[3*numPixels + 3]; 752 | if (buf) 753 | { 754 | memcpy(buf, imageFrame, (size_t)(3*numPixels)); 755 | Uint8 *s = buf; // source 756 | Uint8 *r = imageFrame; // red plane 757 | Uint8 *g = imageFrame + numPixels; // green plane 758 | Uint8 *b = imageFrame + (2*numPixels); // blue plane 759 | for (unsigned long i=numPixels; i; i--) 760 | { 761 | *r++ = *s++; 762 | *g++ = *s++; 763 | *b++ = *s++; 764 | } 765 | delete[] buf; 766 | } else return EC_MemoryExhausted; 767 | return EC_Normal; 768 | } 769 | 770 | 771 | OFCondition DJPEG2KDecoderBase::createPlanarConfiguration1Word( 772 | Uint16 *imageFrame, 773 | Uint16 columns, 774 | Uint16 rows) 775 | { 776 | if (imageFrame == NULL) return EC_IllegalCall; 777 | 778 | unsigned long numPixels = columns * rows; 779 | if (numPixels == 0) return EC_IllegalCall; 780 | 781 | Uint16 *buf = new Uint16[3*numPixels + 3]; 782 | if (buf) 783 | { 784 | memcpy(buf, imageFrame, (size_t)(3*numPixels*sizeof(Uint16))); 785 | Uint16 *s = buf; // source 786 | Uint16 *r = imageFrame; // red plane 787 | Uint16 *g = imageFrame + numPixels; // green plane 788 | Uint16 *b = imageFrame + (2*numPixels); // blue plane 789 | for (unsigned long i=numPixels; i; i--) 790 | { 791 | *r++ = *s++; 792 | *g++ = *s++; 793 | *b++ = *s++; 794 | } 795 | delete[] buf; 796 | } else return EC_MemoryExhausted; 797 | return EC_Normal; 798 | } 799 | 800 | OFCondition DJPEG2KDecoderBase::createPlanarConfiguration0Byte( 801 | Uint8 *imageFrame, 802 | Uint16 columns, 803 | Uint16 rows) 804 | { 805 | if (imageFrame == NULL) return EC_IllegalCall; 806 | 807 | unsigned long numPixels = columns * rows; 808 | if (numPixels == 0) return EC_IllegalCall; 809 | 810 | Uint8 *buf = new Uint8[3*numPixels + 3]; 811 | if (buf) 812 | { 813 | memcpy(buf, imageFrame, (size_t)(3*numPixels)); 814 | Uint8 *t = imageFrame; // target 815 | Uint8 *r = buf; // red plane 816 | Uint8 *g = buf + numPixels; // green plane 817 | Uint8 *b = buf + (2*numPixels); // blue plane 818 | for (unsigned long i=numPixels; i; i--) 819 | { 820 | *t++ = *r++; 821 | *t++ = *g++; 822 | *t++ = *b++; 823 | } 824 | delete[] buf; 825 | } else return EC_MemoryExhausted; 826 | return EC_Normal; 827 | } 828 | 829 | 830 | OFCondition DJPEG2KDecoderBase::createPlanarConfiguration0Word( 831 | Uint16 *imageFrame, 832 | Uint16 columns, 833 | Uint16 rows) 834 | { 835 | if (imageFrame == NULL) return EC_IllegalCall; 836 | 837 | unsigned long numPixels = columns * rows; 838 | if (numPixels == 0) return EC_IllegalCall; 839 | 840 | Uint16 *buf = new Uint16[3*numPixels + 3]; 841 | if (buf) 842 | { 843 | memcpy(buf, imageFrame, (size_t)(3*numPixels*sizeof(Uint16))); 844 | Uint16 *t = imageFrame; // target 845 | Uint16 *r = buf; // red plane 846 | Uint16 *g = buf + numPixels; // green plane 847 | Uint16 *b = buf + (2*numPixels); // blue plane 848 | for (unsigned long i=numPixels; i; i--) 849 | { 850 | *t++ = *r++; 851 | *t++ = *g++; 852 | *t++ = *b++; 853 | } 854 | delete[] buf; 855 | } else return EC_MemoryExhausted; 856 | return EC_Normal; 857 | } 858 | 859 | 860 | OFCondition copyUint32ToUint8( 861 | opj_image_t * image, 862 | Uint8 *imageFrame, 863 | Uint16 columns, 864 | Uint16 rows) 865 | { 866 | if (imageFrame == NULL) return EC_IllegalCall; 867 | 868 | unsigned long numPixels = columns * rows; 869 | if (numPixels == 0) return EC_IllegalCall; 870 | 871 | Uint8 *t = imageFrame; // target 872 | OPJ_INT32 *g = image->comps[0].data; // grey plane 873 | for (unsigned long i=numPixels; i; i--) 874 | { 875 | *t++ = *g++; 876 | } 877 | 878 | return EC_Normal; 879 | } 880 | 881 | OFCondition copyUint32ToUint16( 882 | opj_image_t * image, 883 | Uint16 *imageFrame, 884 | Uint16 columns, 885 | Uint16 rows) 886 | { 887 | if (imageFrame == NULL) return EC_IllegalCall; 888 | 889 | unsigned long numPixels = columns * rows; 890 | if (numPixels == 0) return EC_IllegalCall; 891 | 892 | Uint16 *t = imageFrame; // target 893 | OPJ_INT32 *g = image->comps[0].data; // grey plane 894 | for (unsigned long i=numPixels; i; i--) 895 | { 896 | *t++ = *g++; 897 | } 898 | 899 | return EC_Normal; 900 | } 901 | 902 | OFCondition copyRGBUint8ToRGBUint8( 903 | opj_image_t * image, 904 | Uint8 *imageFrame, 905 | Uint16 columns, 906 | Uint16 rows) 907 | { 908 | if (imageFrame == NULL) return EC_IllegalCall; 909 | 910 | unsigned long numPixels = columns * rows; 911 | if (numPixels == 0) return EC_IllegalCall; 912 | 913 | Uint8 *t = imageFrame; // target 914 | OPJ_INT32 *r = image->comps[0].data; // red plane 915 | OPJ_INT32 *g = image->comps[1].data; // green plane 916 | OPJ_INT32 *b = image->comps[2].data; // blue plane 917 | for (unsigned long i=numPixels; i; i--) 918 | { 919 | *t++ = *r++; 920 | *t++ = *g++; 921 | *t++ = *b++; 922 | } 923 | 924 | return EC_Normal; 925 | } 926 | 927 | OFCondition copyRGBUint8ToRGBUint8Planar( 928 | opj_image_t * image, 929 | Uint8 *imageFrame, 930 | Uint16 columns, 931 | Uint16 rows) 932 | { 933 | if (imageFrame == NULL) return EC_IllegalCall; 934 | 935 | unsigned long numPixels = columns * rows; 936 | if (numPixels == 0) return EC_IllegalCall; 937 | 938 | Uint8 *t = imageFrame; // target 939 | for(unsigned long j=0; j<3; j++) 940 | { 941 | OPJ_INT32 *r = image->comps[j].data; // color plane 942 | for (unsigned long i=numPixels; i; i--) 943 | { 944 | *t++ = *r++; 945 | } 946 | } 947 | return EC_Normal; 948 | } -------------------------------------------------------------------------------- /djcodece.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015-2017 Ing-Long Eric Kuo 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | 17 | * Module: dcmjpeg2k 18 | * 19 | * Author: Ing-Long Eric Kuo 20 | * 21 | * Purpose: codec classes for JPEG 2000 encoders. 22 | * 23 | */ 24 | 25 | #include "dcmtk/config/osconfig.h" 26 | #include "fmjpeg2k/djcodece.h" 27 | 28 | // ofstd includes 29 | #include "dcmtk/ofstd/oflist.h" 30 | #include "dcmtk/ofstd/ofstd.h" 31 | #include "dcmtk/ofstd/ofstream.h" 32 | #include "dcmtk/ofstd/offile.h" /* for class OFFile */ 33 | #include "dcmtk/ofstd/ofbmanip.h" 34 | 35 | #include 36 | #include "dcmtk/ofstd/ofstdinc.h" 37 | 38 | // dcmdata includes 39 | #include "dcmtk/dcmdata/dcdatset.h" /* for class DcmDataset */ 40 | #include "dcmtk/dcmdata/dcdeftag.h" /* for tag constants */ 41 | #include "dcmtk/dcmdata/dcovlay.h" /* for class DcmOverlayData */ 42 | #include "dcmtk/dcmdata/dcpixseq.h" /* for class DcmPixelSequence */ 43 | #include "dcmtk/dcmdata/dcpxitem.h" /* for class DcmPixelItem */ 44 | #include "dcmtk/dcmdata/dcuid.h" /* for dcmGenerateUniqueIdentifer()*/ 45 | #include "dcmtk/dcmdata/dcvrcs.h" /* for class DcmCodeString */ 46 | #include "dcmtk/dcmdata/dcvrds.h" /* for class DcmDecimalString */ 47 | #include "dcmtk/dcmdata/dcvrlt.h" /* for class DcmLongText */ 48 | #include "dcmtk/dcmdata/dcvrst.h" /* for class DcmShortText */ 49 | #include "dcmtk/dcmdata/dcvrus.h" /* for class DcmUnsignedShort */ 50 | #include "dcmtk/dcmdata/dcswap.h" /* for swapIfNecessary */ 51 | 52 | // dcmjpls includes 53 | #include "fmjpeg2k/djcparam.h" /* for class DJP2KCodecParameter */ 54 | #include "fmjpeg2k/djrparam.h" /* for class D2RepresentationParameter */ 55 | #include "fmjpeg2k/djerror.h" /* for private class DJLSError */ 56 | 57 | // dcmimgle includes 58 | #include "dcmtk/dcmimgle/dcmimage.h" /* for class DicomImage */ 59 | 60 | // JPEG-2000 library (OpenJPEG) includes 61 | #include "openjpeg.h" 62 | #include "fmjpeg2k/memory_file.h" 63 | 64 | BEGIN_EXTERN_C 65 | #ifdef HAVE_FCNTL_H 66 | #include /* for O_RDONLY */ 67 | #endif 68 | #ifdef HAVE_SYS_TYPES_H 69 | #include /* required for sys/stat.h */ 70 | #endif 71 | #ifdef HAVE_SYS_STAT_H 72 | #include /* for stat, fstat */ 73 | #endif 74 | END_EXTERN_C 75 | 76 | 77 | E_TransferSyntax DJPEG2KLosslessEncoder::supportedTransferSyntax() const 78 | { 79 | return EXS_JPEG2000LosslessOnly; 80 | } 81 | 82 | E_TransferSyntax DJPEG2KNearLosslessEncoder::supportedTransferSyntax() const 83 | { 84 | return EXS_JPEG2000; 85 | } 86 | 87 | // -------------------------------------------------------------------------- 88 | 89 | DJPEG2KEncoderBase::DJPEG2KEncoderBase() 90 | : DcmCodec() 91 | { 92 | } 93 | 94 | 95 | DJPEG2KEncoderBase::~DJPEG2KEncoderBase() 96 | { 97 | } 98 | 99 | 100 | OFBool DJPEG2KEncoderBase::canChangeCoding( 101 | const E_TransferSyntax oldRepType, 102 | const E_TransferSyntax newRepType) const 103 | { 104 | // this codec only handles conversion from uncompressed to JPEG 2000. 105 | DcmXfer oldRep(oldRepType); 106 | return (oldRep.isNotEncapsulated() && (newRepType == supportedTransferSyntax())); 107 | } 108 | 109 | 110 | OFCondition DJPEG2KEncoderBase::decode( 111 | const DcmRepresentationParameter * /* fromRepParam */, 112 | DcmPixelSequence * /* pixSeq */, 113 | DcmPolymorphOBOW& /* uncompressedPixelData */, 114 | const DcmCodecParameter * /* cp */, 115 | const DcmStack& /* objStack */) const 116 | { 117 | // we are an encoder only 118 | return EC_IllegalCall; 119 | } 120 | 121 | 122 | OFCondition DJPEG2KEncoderBase::decode( 123 | const DcmRepresentationParameter * fromRepParam, 124 | DcmPixelSequence * pixSeq, 125 | DcmPolymorphOBOW& uncompressedPixelData, 126 | const DcmCodecParameter * cp, 127 | const DcmStack& objStack, 128 | OFBool& removeOldRep) const 129 | { 130 | return EC_IllegalCall; 131 | } 132 | 133 | 134 | OFCondition DJPEG2KEncoderBase::decodeFrame( 135 | const DcmRepresentationParameter * /* fromParam */ , 136 | DcmPixelSequence * /* fromPixSeq */ , 137 | const DcmCodecParameter * /* cp */ , 138 | DcmItem * /* dataset */ , 139 | Uint32 /* frameNo */ , 140 | Uint32& /* startFragment */ , 141 | void * /* buffer */ , 142 | Uint32 /* bufSize */ , 143 | OFString& /* decompressedColorModel */ ) const 144 | { 145 | // we are an encoder only 146 | return EC_IllegalCall; 147 | } 148 | 149 | 150 | OFCondition DJPEG2KEncoderBase::encode( 151 | const E_TransferSyntax /* fromRepType */, 152 | const DcmRepresentationParameter * /* fromRepParam */, 153 | DcmPixelSequence * /* fromPixSeq */, 154 | const DcmRepresentationParameter * /* toRepParam */, 155 | DcmPixelSequence * & /* toPixSeq */, 156 | const DcmCodecParameter * /* cp */, 157 | DcmStack & /* objStack */) const 158 | { 159 | // we don't support re-coding for now. 160 | return EC_IllegalCall; 161 | } 162 | 163 | 164 | OFCondition DJPEG2KEncoderBase::encode( 165 | const E_TransferSyntax fromRepType, 166 | const DcmRepresentationParameter * fromRepParam, 167 | DcmPixelSequence * fromPixSeq, 168 | const DcmRepresentationParameter * toRepParam, 169 | DcmPixelSequence * & toPixSeq, 170 | const DcmCodecParameter * cp, 171 | DcmStack & objStack, 172 | OFBool& removeOldRep) const 173 | { 174 | return EC_IllegalCall; 175 | } 176 | 177 | 178 | OFCondition DJPEG2KEncoderBase::encode( 179 | const Uint16 * pixelData, 180 | const Uint32 length, 181 | const DcmRepresentationParameter * toRepParam, 182 | DcmPixelSequence * & pixSeq, 183 | const DcmCodecParameter *cp, 184 | DcmStack & objStack) const 185 | { 186 | OFCondition result = EC_Normal; 187 | FMJPEG2KRepresentationParameter defRep; 188 | 189 | // retrieve pointer to dataset from parameter stack 190 | DcmStack localStack(objStack); 191 | (void)localStack.pop(); // pop pixel data element from stack 192 | DcmObject *dobject = localStack.pop(); // this is the item in which the pixel data is located 193 | if ((!dobject)||((dobject->ident()!= EVR_dataset) && (dobject->ident()!= EVR_item))) return EC_InvalidTag; 194 | DcmItem *dataset = OFstatic_cast(DcmItem *, dobject); 195 | 196 | // assume we can cast the codec and representation parameters to what we need 197 | const DJPEG2KCodecParameter *djcp = OFreinterpret_cast(const DJPEG2KCodecParameter *, cp); 198 | const FMJPEG2KRepresentationParameter *djrp = OFreinterpret_cast(const FMJPEG2KRepresentationParameter *, toRepParam); 199 | double compressionRatio = 0.0; 200 | 201 | if (!djrp) 202 | djrp = &defRep; 203 | 204 | if (supportedTransferSyntax() == EXS_JPEG2000LosslessOnly || djrp->useLosslessProcess()) 205 | { 206 | if (djcp->cookedEncodingPreferred()) 207 | result = RenderedEncode(pixelData, length, dataset, djrp, pixSeq, djcp, compressionRatio); 208 | else result = losslessRawEncode(pixelData, length, dataset, djrp, pixSeq, djcp, compressionRatio); 209 | } 210 | else 211 | { 212 | // near-lossless mode always uses the "cooked" encoder since this one is guaranteed not to "mix" 213 | // overlays and pixel data in one cell subjected to lossy compression. 214 | result = RenderedEncode(pixelData, length, dataset, djrp, pixSeq, djcp, compressionRatio); 215 | } 216 | 217 | // the following operations do not affect the Image Pixel Module 218 | // but other modules such as SOP Common. We only perform these 219 | // changes if we're on the main level of the dataset, 220 | // which should always identify itself as dataset, not as item. 221 | if (result.good() && dataset->ident() == EVR_dataset) 222 | { 223 | if (result.good()) 224 | { 225 | if (supportedTransferSyntax() == EXS_JPEG2000LosslessOnly || supportedTransferSyntax() == EXS_JPEG2000MulticomponentLosslessOnly || djrp->useLosslessProcess()) 226 | { 227 | // lossless process - create new UID if mode is EUC_always or if we're converting to Secondary Capture 228 | if (djcp->getConvertToSC() || (djcp->getUIDCreation() == EJ2KUC_always)) 229 | result = DcmCodec::newInstance(dataset, "DCM", "121320", "Uncompressed predecessor"); 230 | } 231 | else 232 | { 233 | // lossy process - create new UID unless mode is EUC_never and we're not converting to Secondary Capture 234 | if (djcp->getConvertToSC() || (djcp->getUIDCreation() != EJ2KUC_never)) 235 | result = DcmCodec::newInstance(dataset, "DCM", "121320", "Uncompressed predecessor"); 236 | 237 | // update image type 238 | if (result.good()) result = DcmCodec::updateImageType(dataset); 239 | 240 | // update derivation description 241 | if (result.good()) result = updateDerivationDescription(dataset, djrp, compressionRatio); 242 | 243 | // update lossy compression ratio 244 | if (result.good()) result = updateLossyCompressionRatio(dataset, compressionRatio); 245 | } 246 | } 247 | 248 | // convert to Secondary Capture if requested by user. 249 | // This method creates a new SOP class UID, so it should be executed 250 | // after the call to newInstance() which creates a Source Image Sequence. 251 | if (result.good() && djcp->getConvertToSC()) result = DcmCodec::convertToSecondaryCapture(dataset); 252 | } 253 | 254 | return result; 255 | } 256 | 257 | 258 | OFCondition DJPEG2KEncoderBase::encode( 259 | const Uint16 * pixelData, 260 | const Uint32 length, 261 | const DcmRepresentationParameter * toRepParam, 262 | DcmPixelSequence * & pixSeq, 263 | const DcmCodecParameter *cp, 264 | DcmStack & objStack, 265 | OFBool& removeOldRep) const 266 | { 267 | // removeOldRep is left as it is, pixel data in original DICOM dataset is not modified 268 | return encode(pixelData, length, toRepParam, pixSeq, cp, objStack); 269 | } 270 | 271 | 272 | OFCondition DJPEG2KEncoderBase::determineDecompressedColorModel( 273 | const DcmRepresentationParameter * /* fromParam */, 274 | DcmPixelSequence * /* fromPixSeq */, 275 | const DcmCodecParameter * /* cp */, 276 | DcmItem * /* dataset */, 277 | OFString & /* decompressedColorModel */) const 278 | { 279 | return EC_IllegalCall; 280 | } 281 | 282 | 283 | OFCondition DJPEG2KEncoderBase::adjustOverlays( 284 | DcmItem *dataset, 285 | DicomImage& image) const 286 | { 287 | if (dataset == NULL) return EC_IllegalCall; 288 | 289 | unsigned int overlayCount = image.getOverlayCount(); 290 | if (overlayCount > 0) 291 | { 292 | Uint16 group = 0; 293 | DcmStack stack; 294 | unsigned long bytesAllocated = 0; 295 | Uint8 *buffer = NULL; 296 | unsigned int width = 0; 297 | unsigned int height = 0; 298 | unsigned long frames = 0; 299 | DcmElement *elem = NULL; 300 | OFCondition result = EC_Normal; 301 | 302 | // adjust overlays (prior to grayscale compression) 303 | for (unsigned int i=0; i < overlayCount; i++) 304 | { 305 | // check if current overlay is embedded in pixel data 306 | group = OFstatic_cast(Uint16, image.getOverlayGroupNumber(i)); 307 | stack.clear(); 308 | if ((dataset->search(DcmTagKey(group, 0x3000), stack, ESM_fromHere, OFFalse)).bad()) 309 | { 310 | // separate Overlay Data not found. Assume overlay is embedded. 311 | bytesAllocated = image.create6xxx3000OverlayData(buffer, i, width, height, frames); 312 | if (bytesAllocated > 0) 313 | { 314 | elem = new DcmOverlayData(DcmTagKey(group, 0x3000)); // DCM_OverlayData 315 | if (elem) 316 | { 317 | result = elem->putUint8Array(buffer, bytesAllocated); 318 | delete[] buffer; 319 | if (result.good()) 320 | { 321 | dataset->insert(elem, OFTrue /*replaceOld*/); 322 | // DCM_OverlayBitsAllocated 323 | result = dataset->putAndInsertUint16(DcmTagKey(group, 0x0100), 1); 324 | // DCM_OverlayBitPosition 325 | if (result.good()) result = dataset->putAndInsertUint16(DcmTagKey(group, 0x0102), 0); 326 | } 327 | else 328 | { 329 | delete elem; 330 | return result; 331 | } 332 | } 333 | else 334 | { 335 | delete[] buffer; 336 | return EC_MemoryExhausted; 337 | } 338 | } 339 | else return EC_IllegalCall; 340 | } 341 | } 342 | } 343 | return EC_Normal; 344 | } 345 | 346 | 347 | OFCondition DJPEG2KEncoderBase::updateLossyCompressionRatio( 348 | DcmItem *dataset, 349 | double ratio) const 350 | { 351 | if (dataset == NULL) return EC_IllegalCall; 352 | 353 | // set Lossy Image Compression to "01" (see DICOM part 3, C.7.6.1.1.5) 354 | OFCondition result = dataset->putAndInsertString(DCM_LossyImageCompression, "01"); 355 | if (result.bad()) return result; 356 | 357 | // set Lossy Image Compression Ratio 358 | OFString s; 359 | const char *oldRatio = NULL; 360 | if ((dataset->findAndGetString(DCM_LossyImageCompressionRatio, oldRatio)).good() && oldRatio) 361 | { 362 | s = oldRatio; 363 | s += "\\"; 364 | } 365 | 366 | // append lossy compression ratio 367 | char buf[64]; 368 | OFStandard::ftoa(buf, sizeof(buf), ratio, OFStandard::ftoa_uppercase, 0, 5); 369 | s += buf; 370 | 371 | result = dataset->putAndInsertString(DCM_LossyImageCompressionRatio, s.c_str()); 372 | if (result.bad()) return result; 373 | 374 | // count VM of lossy image compression ratio 375 | size_t i; 376 | size_t s_vm = 0; 377 | size_t s_sz = s.size(); 378 | for (i = 0; i < s_sz; ++i) 379 | if (s[i] == '\\') ++s_vm; 380 | 381 | // set Lossy Image Compression Method 382 | const char *oldMethod = NULL; 383 | OFString m; 384 | if ((dataset->findAndGetString(DCM_LossyImageCompressionMethod, oldMethod)).good() && oldMethod) 385 | { 386 | m = oldMethod; 387 | m += "\\"; 388 | } 389 | 390 | // count VM of lossy image compression method 391 | size_t m_vm = 0; 392 | size_t m_sz = m.size(); 393 | for (i = 0; i < m_sz; ++i) 394 | if (m[i] == '\\') ++m_vm; 395 | 396 | // make sure that VM of Compression Method is not smaller than VM of Compression Ratio 397 | while (m_vm++ < s_vm) m += "\\"; 398 | 399 | m += "ISO_14495_1"; 400 | return dataset->putAndInsertString(DCM_LossyImageCompressionMethod, m.c_str()); 401 | } 402 | 403 | 404 | OFCondition DJPEG2KEncoderBase::updateDerivationDescription( 405 | DcmItem *dataset, 406 | const FMJPEG2KRepresentationParameter *djrp, 407 | double ratio) const 408 | { 409 | OFString derivationDescription; 410 | char buf[64]; 411 | 412 | derivationDescription = "near lossless JPEG-2000 compression, factor "; 413 | OFStandard::ftoa(buf, sizeof(buf), ratio, OFStandard::ftoa_uppercase, 0, 5); 414 | derivationDescription += buf; 415 | sprintf(buf, " (PSNR=%lu)", OFstatic_cast(unsigned long, djrp->getnearlosslessPSNR())); 416 | derivationDescription += buf; 417 | 418 | // append old Derivation Description, if any 419 | const char *oldDerivation = NULL; 420 | if ((dataset->findAndGetString(DCM_DerivationDescription, oldDerivation)).good() && oldDerivation) 421 | { 422 | derivationDescription += " ["; 423 | derivationDescription += oldDerivation; 424 | derivationDescription += "]"; 425 | if (derivationDescription.length() > 1024) 426 | { 427 | // ST is limited to 1024 characters, cut off tail 428 | derivationDescription.erase(1020); 429 | derivationDescription += "...]"; 430 | } 431 | } 432 | 433 | OFCondition result = dataset->putAndInsertString(DCM_DerivationDescription, derivationDescription.c_str()); 434 | if (result.good()) result = DcmCodec::insertCodeSequence(dataset, DCM_DerivationCodeSequence, "DCM", "113040", "Lossy Compression"); 435 | return result; 436 | } 437 | 438 | 439 | OFCondition DJPEG2KEncoderBase::losslessRawEncode( 440 | const Uint16 *pixelData, 441 | const Uint32 length, 442 | DcmItem *dataset, 443 | const FMJPEG2KRepresentationParameter *djrp, 444 | DcmPixelSequence * & pixSeq, 445 | const DJPEG2KCodecParameter *djcp, 446 | double& compressionRatio) const 447 | { 448 | compressionRatio = 0.0; // initialize if something goes wrong 449 | 450 | // determine image properties 451 | Uint16 bitsAllocated = 0; 452 | Uint16 bitsStored = 0; 453 | Uint16 bytesAllocated = 0; 454 | Uint16 samplesPerPixel = 0; 455 | Uint16 planarConfiguration = 0; 456 | Uint16 columns = 0; 457 | Uint16 rows = 0; 458 | Sint32 numberOfFrames = 1; 459 | OFBool byteSwapped = OFFalse; // true if we have byte-swapped the original pixel data 460 | OFString photometricInterpretation; 461 | Uint16 pixelRepresentation = 0; 462 | OFCondition result = dataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated); 463 | if (result.good()) result = dataset->findAndGetUint16(DCM_BitsStored, bitsStored); 464 | if (result.good()) result = dataset->findAndGetUint16(DCM_SamplesPerPixel, samplesPerPixel); 465 | if (result.good()) result = dataset->findAndGetUint16(DCM_Columns, columns); 466 | if (result.good()) result = dataset->findAndGetUint16(DCM_Rows, rows); 467 | if (result.good()) dataset->findAndGetUint16(DCM_PixelRepresentation, pixelRepresentation); 468 | if (result.good()) result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, photometricInterpretation); 469 | if (result.good()) 470 | { 471 | result = dataset->findAndGetSint32(DCM_NumberOfFrames, numberOfFrames); 472 | if (result.bad() || numberOfFrames < 1) numberOfFrames = 1; 473 | result = EC_Normal; 474 | } 475 | if (result.good() && (samplesPerPixel > 1)) 476 | { 477 | result = dataset->findAndGetUint16(DCM_PlanarConfiguration, planarConfiguration); 478 | } 479 | 480 | if (result.good()) 481 | { 482 | // check if bitsAllocated is 8 or 16 or 32 - we don't handle anything else 483 | if (bitsAllocated == 8) 484 | { 485 | bytesAllocated = 1; 486 | } 487 | else if (bitsAllocated == 16) 488 | { 489 | bytesAllocated = 2; 490 | } 491 | else if (bitsAllocated == 32) 492 | { 493 | bytesAllocated = 4; 494 | } 495 | else 496 | { 497 | if (photometricInterpretation == "MONOCHROME1" || 498 | photometricInterpretation == "MONOCHROME2" || 499 | photometricInterpretation == "RGB" || 500 | photometricInterpretation == "YBR_FULL") 501 | { 502 | // A bitsAllocated value that we don't handle, but a color model that indicates 503 | // that the cooked encoder could handle this case. Fall back to cooked encoder. 504 | return RenderedEncode(pixelData, length, dataset, djrp, pixSeq, djcp, compressionRatio); 505 | } 506 | 507 | // an image that is not supported by either the raw or the cooked encoder. 508 | result = EC_J2KUnsupportedImageType; 509 | } 510 | 511 | // make sure that all the descriptive attributes have sensible values 512 | if ((columns < 1)||(rows < 1)||(samplesPerPixel < 1)) result = EC_J2KUnsupportedImageType; 513 | 514 | // make sure that we have at least as many bytes of pixel data as we expect 515 | if (bytesAllocated * samplesPerPixel * columns * rows * 516 | OFstatic_cast(unsigned long,numberOfFrames) > length) 517 | result = EC_J2KUncompressedBufferTooSmall; 518 | } 519 | 520 | DcmPixelSequence *pixelSequence = NULL; 521 | DcmPixelItem *offsetTable = NULL; 522 | 523 | // create initial pixel sequence 524 | if (result.good()) 525 | { 526 | pixelSequence = new DcmPixelSequence(DcmTag(DCM_PixelData,EVR_OB)); 527 | if (pixelSequence == NULL) result = EC_MemoryExhausted; 528 | else 529 | { 530 | // create empty offset table 531 | offsetTable = new DcmPixelItem(DcmTag(DCM_Item,EVR_OB)); 532 | if (offsetTable == NULL) result = EC_MemoryExhausted; 533 | else pixelSequence->insert(offsetTable); 534 | } 535 | } 536 | 537 | DcmOffsetList offsetList; 538 | unsigned long compressedSize = 0; 539 | unsigned long compressedFrameSize = 0; 540 | double uncompressedSize = 0.0; 541 | 542 | // render and compress each frame 543 | if (result.good()) 544 | { 545 | 546 | // byte swap pixel data to little endian if bits allocate is 8 547 | if ((gLocalByteOrder == EBO_BigEndian) && (bitsAllocated == 8)) 548 | { 549 | swapIfNecessary(EBO_LittleEndian, gLocalByteOrder, OFstatic_cast(void *, OFconst_cast(Uint16 *, pixelData)), length, sizeof(Uint16)); 550 | byteSwapped = OFTrue; 551 | } 552 | 553 | unsigned long frameCount = OFstatic_cast(unsigned long, numberOfFrames); 554 | unsigned long frameSize = columns * rows * samplesPerPixel * bytesAllocated; 555 | const Uint8 *framePointer = OFreinterpret_cast(const Uint8 *, pixelData); 556 | 557 | // compute original image size in bytes, ignoring any padding bits. 558 | uncompressedSize = columns * rows * samplesPerPixel * bitsStored * frameCount / 8.0; 559 | 560 | for (unsigned long i=0; (igetCreateOffsetTable())) 583 | { 584 | result = offsetTable->createOffsetTable(offsetList); 585 | } 586 | 587 | if (compressedSize > 0) compressionRatio = uncompressedSize / compressedSize; 588 | 589 | // byte swap pixel data back to local endian if necessary 590 | if (byteSwapped) 591 | { 592 | swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, OFstatic_cast(void *, OFconst_cast(Uint16 *, pixelData)), length, sizeof(Uint16)); 593 | } 594 | 595 | return result; 596 | } 597 | 598 | OFCondition frametoimage(const Uint8 *framePointer, int planarConfiguration, OFString photometricInterpretation, int samplesPerPixel, int width, int height, int bitsAllocated, OFBool isSigned, opj_cparameters_t *parameters, opj_image_t **ret_image); 599 | opj_image_t *frameToImage2(const Uint8 *framePointer, int width, int height, opj_cparameters_t *parameters); 600 | opj_image_t *frameToImage3(const Uint8 *framePointer, int width, int height, opj_cparameters_t *parameters); 601 | 602 | OFCondition DJPEG2KEncoderBase::compressRawFrame( 603 | const Uint8 *framePointer, 604 | Uint16 bitsAllocated, 605 | Uint16 width, 606 | Uint16 height, 607 | Uint16 samplesPerPixel, 608 | Uint16 planarConfiguration, 609 | OFBool pixelRepresentation, 610 | const OFString& photometricInterpretation, 611 | DcmPixelSequence *pixelSequence, 612 | DcmOffsetList &offsetList, 613 | unsigned long &compressedSize, 614 | const DJPEG2KCodecParameter *djcp) const 615 | { 616 | OFCondition result = EC_Normal; 617 | Uint16 bytesAllocated = bitsAllocated / 8; 618 | Uint32 frameSize = width*height*bytesAllocated*samplesPerPixel; 619 | Uint32 fragmentSize = djcp->getFragmentSize(); 620 | opj_cparameters_t parameters; 621 | opj_image_t *image = NULL; 622 | 623 | opj_set_default_encoder_parameters(¶meters); 624 | 625 | result = frametoimage(framePointer, planarConfiguration, photometricInterpretation, samplesPerPixel, width, height, bitsAllocated, pixelRepresentation, ¶meters, &image); 626 | 627 | if (result.good()) 628 | { 629 | // set lossless 630 | parameters.tcp_numlayers = 1; 631 | parameters.tcp_rates[0] = 0; 632 | parameters.cp_disto_alloc = 1; 633 | 634 | if(djcp->getUseCustomOptions()) 635 | { 636 | parameters.cblockw_init = djcp->get_cblkwidth(); 637 | parameters.cblockh_init = djcp->get_cblkheight(); 638 | } 639 | 640 | // turn on/off MCT depending on transfer syntax 641 | if(supportedTransferSyntax() == EXS_JPEG2000LosslessOnly) 642 | parameters.tcp_mct = 0; 643 | else if(supportedTransferSyntax() == EXS_JPEG2000MulticomponentLosslessOnly) 644 | parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; 645 | 646 | // We have no idea how big the compressed pixel data will be and we have no 647 | // way to find out, so we just allocate a buffer large enough for the raw data 648 | // plus a little more for JPEG metadata. 649 | // Yes, this is way too much for just a little JPEG metadata, but some 650 | // test-images showed that the buffer previously was too small. Plus, at some 651 | // places charls fails to do proper bounds checking and writes behind the end 652 | // of the buffer (sometimes way behind its end...). 653 | size_t size = frameSize + 1024; 654 | Uint8 *buffer = new Uint8[size]; 655 | 656 | // Set up the information structure for OpenJPEG 657 | opj_stream_t *l_stream = NULL; 658 | opj_codec_t* l_codec = NULL; 659 | l_codec = opj_create_compress(OPJ_CODEC_J2K); 660 | 661 | opj_set_info_handler(l_codec, msg_callback, NULL); 662 | opj_set_warning_handler(l_codec, msg_callback, NULL); 663 | opj_set_error_handler(l_codec, msg_callback, NULL); 664 | 665 | if (result.good() && !opj_setup_encoder(l_codec, ¶meters, image)) 666 | { 667 | opj_destroy_codec(l_codec); 668 | l_codec = NULL; 669 | result = EC_MemoryExhausted; 670 | } 671 | 672 | DecodeData mysrc((unsigned char*)buffer, size); 673 | l_stream = opj_stream_create_memory_stream(&mysrc, size, OPJ_FALSE); 674 | 675 | if(!opj_start_compress(l_codec,image,l_stream)) 676 | { 677 | result = EC_CorruptedData; 678 | } 679 | 680 | if(result.good() && !opj_encode(l_codec, l_stream)) 681 | { 682 | result = EC_InvalidStream; 683 | } 684 | 685 | if(result.good() && opj_end_compress(l_codec, l_stream)) 686 | { 687 | result = EC_Normal; 688 | } 689 | 690 | opj_stream_destroy(l_stream); l_stream = NULL; 691 | opj_destroy_codec(l_codec); l_codec = NULL; 692 | opj_image_destroy(image); image = NULL; 693 | 694 | size = mysrc.offset; 695 | 696 | if (result.good()) 697 | { 698 | // 'size' now contains the size of the compressed data in buffer 699 | compressedSize = size; 700 | result = pixelSequence->storeCompressedFrame(offsetList, buffer, size, fragmentSize); 701 | } 702 | 703 | delete[] buffer; 704 | } 705 | 706 | return result; 707 | } 708 | 709 | 710 | OFCondition DJPEG2KEncoderBase::RenderedEncode( 711 | const Uint16 *pixelData, 712 | const Uint32 length, 713 | DcmItem *dataset, 714 | const FMJPEG2KRepresentationParameter *djrp, 715 | DcmPixelSequence * & pixSeq, 716 | const DJPEG2KCodecParameter *djcp, 717 | double& compressionRatio) const 718 | { 719 | compressionRatio = 0.0; // initialize if something goes wrong 720 | 721 | // determine a few image properties 722 | OFString photometricInterpretation; 723 | Uint16 bitsAllocated = 0; 724 | OFCondition result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, photometricInterpretation); 725 | if (result.good()) result = dataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated); 726 | if (result.bad()) return result; 727 | 728 | // The cooked encoder only handles the following photometic interpretations 729 | if (photometricInterpretation != "MONOCHROME1" && 730 | photometricInterpretation != "MONOCHROME2" && 731 | photometricInterpretation != "RGB" && 732 | photometricInterpretation != "YBR_FULL") 733 | { 734 | // a photometric interpretation that we don't handle. Fall back to raw encoder (unless in near-lossless mode) 735 | return losslessRawEncode(pixelData, length, dataset, djrp, pixSeq, djcp, compressionRatio); 736 | } 737 | 738 | Uint16 pixelRepresentation = 0; 739 | result = dataset->findAndGetUint16(DCM_PixelRepresentation, pixelRepresentation); 740 | if (result.bad()) return result; 741 | 742 | DcmPixelSequence *pixelSequence = NULL; 743 | DcmPixelItem *offsetTable = NULL; 744 | 745 | // ignore modality transformation (rescale slope/intercept or LUT) stored in the dataset 746 | unsigned long flags = CIF_IgnoreModalityTransformation; 747 | // don't convert YCbCr (Full and Full 4:2:2) color images to RGB 748 | flags |= CIF_KeepYCbCrColorModel; 749 | // Don't optimize memory usage, but keep using the same bitsAllocated. 750 | // Without this, the DICOM and the JPEG-2000 value for bitsAllocated could 751 | // differ and the decoder would error out. 752 | flags |= CIF_UseAbsolutePixelRange; 753 | 754 | DicomImage *dimage = new DicomImage(dataset, EXS_LittleEndianImplicit, flags); // read all frames 755 | if (dimage == NULL) return EC_MemoryExhausted; 756 | if (dimage->getStatus() != EIS_Normal) 757 | { 758 | delete dimage; 759 | return EC_IllegalCall; 760 | } 761 | 762 | // create overlay data for embedded overlays 763 | result = adjustOverlays(dataset, *dimage); 764 | 765 | // determine number of bits per sample 766 | int bitsPerSample = dimage->getDepth(); 767 | if (result.good() && (bitsPerSample > 16)) result = EC_J2KUnsupportedBitDepth; 768 | 769 | // create initial pixel sequence 770 | if (result.good()) 771 | { 772 | pixelSequence = new DcmPixelSequence(DcmTag(DCM_PixelData,EVR_OB)); 773 | if (pixelSequence == NULL) result = EC_MemoryExhausted; 774 | else 775 | { 776 | // create empty offset table 777 | offsetTable = new DcmPixelItem(DcmTag(DCM_Item,EVR_OB)); 778 | if (offsetTable == NULL) result = EC_MemoryExhausted; 779 | else pixelSequence->insert(offsetTable); 780 | } 781 | } 782 | 783 | DcmOffsetList offsetList; 784 | unsigned long compressedSize = 0; 785 | unsigned long compressedFrameSize = 0; 786 | double uncompressedSize = 0.0; 787 | 788 | // render and compress each frame 789 | if (result.good()) 790 | { 791 | unsigned long frameCount = dimage->getFrameCount(); 792 | 793 | // compute original image size in bytes, ignoring any padding bits. 794 | Uint16 samplesPerPixel = 0; 795 | if ((dataset->findAndGetUint16(DCM_SamplesPerPixel, samplesPerPixel)).bad()) samplesPerPixel = 1; 796 | uncompressedSize = dimage->getWidth() * dimage->getHeight() * 797 | bitsPerSample * frameCount * samplesPerPixel / 8.0; 798 | 799 | for (unsigned long i=0; (igetCreateOffsetTable())) 820 | { 821 | result = offsetTable->createOffsetTable(offsetList); 822 | } 823 | 824 | // adapt attributes in image pixel module 825 | if (result.good()) 826 | { 827 | // adjustments needed for both color and monochrome 828 | if (bitsPerSample > 8) 829 | result = dataset->putAndInsertUint16(DCM_BitsAllocated, 16); 830 | else 831 | result = dataset->putAndInsertUint16(DCM_BitsAllocated, 8); 832 | if (result.good()) result = dataset->putAndInsertUint16(DCM_BitsStored, bitsPerSample); 833 | if (result.good()) result = dataset->putAndInsertUint16(DCM_HighBit, bitsPerSample-1); 834 | } 835 | 836 | if (compressedSize > 0) compressionRatio = uncompressedSize / compressedSize; 837 | delete dimage; 838 | return result; 839 | } 840 | 841 | 842 | OFCondition DJPEG2KEncoderBase::compressRenderedFrame( 843 | DcmPixelSequence *pixelSequence, 844 | DicomImage *dimage, 845 | const OFString& photometricInterpretation, 846 | DcmOffsetList &offsetList, 847 | unsigned long &compressedSize, 848 | const DJPEG2KCodecParameter *djcp, 849 | Uint32 frame, 850 | const FMJPEG2KRepresentationParameter *djrp) const 851 | { 852 | if (dimage == NULL) return EC_IllegalCall; 853 | 854 | // access essential image parameters 855 | int width = dimage->getWidth(); 856 | int height = dimage->getHeight(); 857 | int depth = dimage->getDepth(); 858 | if ((depth < 1) || (depth > 16)) return EC_J2KUnsupportedBitDepth; 859 | 860 | Uint32 fragmentSize = djcp->getFragmentSize(); 861 | 862 | const DiPixel *dinter = dimage->getInterData(); 863 | if (dinter == NULL) return EC_IllegalCall; 864 | 865 | // There should be no other possibilities 866 | int samplesPerPixel = dinter->getPlanes(); 867 | if (samplesPerPixel != 1 && samplesPerPixel != 3) return EC_IllegalCall; 868 | 869 | // get pointer to internal raw representation of image data 870 | const void *draw = dinter->getData(); 871 | if (draw == NULL) return EC_IllegalCall; 872 | 873 | OFCondition result = EC_Normal; 874 | 875 | const void *planes[3] = {NULL, NULL, NULL}; 876 | if (samplesPerPixel == 3) 877 | { 878 | // for color images, dinter->getData() returns a pointer to an array 879 | // of pointers pointing to the real plane data 880 | const void * const * draw_array = OFstatic_cast(const void * const *,draw); 881 | planes[0] = draw_array[0]; 882 | planes[1] = draw_array[1]; 883 | planes[2] = draw_array[2]; 884 | } 885 | else 886 | { 887 | // for monochrome images, dinter->getData() directly returns a pointer 888 | // to the single monochrome plane. 889 | planes[0] = draw; 890 | } 891 | 892 | // This is the buffer with the uncompressed pixel data 893 | Uint8 *buffer; 894 | size_t buffer_size; 895 | 896 | Uint32 framesize = dimage->getWidth() * dimage->getHeight(); 897 | switch(dinter->getRepresentation()) 898 | { 899 | case EPR_Uint8: 900 | case EPR_Sint8: 901 | { 902 | // image representation is 8 bit signed or unsigned 903 | if (samplesPerPixel == 1) 904 | { 905 | const Uint8 *yv = OFreinterpret_cast(const Uint8 *, planes[0]) + framesize * frame; 906 | buffer_size = framesize; 907 | buffer = new Uint8[buffer_size]; 908 | memcpy(buffer, yv, framesize); 909 | } 910 | else 911 | { 912 | const Uint8 *rv = OFreinterpret_cast(const Uint8 *, planes[0]) + framesize * frame; 913 | const Uint8 *gv = OFreinterpret_cast(const Uint8 *, planes[1]) + framesize * frame; 914 | const Uint8 *bv = OFreinterpret_cast(const Uint8 *, planes[2]) + framesize * frame; 915 | 916 | buffer_size = framesize * 3; 917 | buffer = new Uint8[buffer_size]; 918 | 919 | size_t i = 0; 920 | for (int row=height; row; --row) 921 | { 922 | for (int col=width; col; --col) 923 | { 924 | buffer[i++] = *rv; 925 | buffer[i++] = *gv; 926 | buffer[i++] = *bv; 927 | 928 | rv++; 929 | gv++; 930 | bv++; 931 | } 932 | } 933 | } 934 | } 935 | break; 936 | case EPR_Uint16: 937 | case EPR_Sint16: 938 | { 939 | // image representation is 16 bit signed or unsigned 940 | if (samplesPerPixel == 1) 941 | { 942 | const Uint16 *yv = OFreinterpret_cast(const Uint16 *, planes[0]) + framesize * frame; 943 | buffer_size = framesize*sizeof(Uint16); 944 | buffer = new Uint8[buffer_size]; 945 | memcpy(buffer, yv, buffer_size); 946 | } 947 | else 948 | { 949 | const Uint16 *rv = OFreinterpret_cast(const Uint16 *, planes[0]) + framesize * frame; 950 | const Uint16 *gv = OFreinterpret_cast(const Uint16 *, planes[1]) + framesize * frame; 951 | const Uint16 *bv = OFreinterpret_cast(const Uint16 *, planes[2]) + framesize * frame; 952 | 953 | buffer_size = framesize * 3; 954 | Uint16 *buffer16 = new Uint16[buffer_size]; 955 | buffer = OFreinterpret_cast(Uint8 *, buffer16); 956 | 957 | // Convert to byte count 958 | buffer_size *= 2; 959 | 960 | size_t i = 0; 961 | for (int row=height; row; --row) 962 | { 963 | for (int col=width; col; --col) 964 | { 965 | buffer16[i++] = *rv; 966 | buffer16[i++] = *gv; 967 | buffer16[i++] = *bv; 968 | 969 | rv++; 970 | gv++; 971 | bv++; 972 | } 973 | } 974 | } 975 | } 976 | break; 977 | default: 978 | // we don't support images with > 16 bits/sample 979 | return EC_J2KUnsupportedBitDepth; 980 | break; 981 | } 982 | 983 | opj_cparameters_t parameters; 984 | opj_image_t *image = NULL; 985 | 986 | opj_set_default_encoder_parameters(¶meters); 987 | 988 | int bitsAllocated = 8; 989 | int pixelRepresentation = 0; 990 | switch(dinter->getRepresentation()) 991 | { 992 | case EPR_Uint8: 993 | bitsAllocated = 8; 994 | pixelRepresentation = 0; 995 | break; 996 | case EPR_Sint8: 997 | bitsAllocated = 8; 998 | pixelRepresentation = 1; 999 | break; 1000 | case EPR_Uint16: 1001 | bitsAllocated = 16; 1002 | pixelRepresentation = 0; 1003 | break; 1004 | case EPR_Sint16: 1005 | bitsAllocated = 16; 1006 | pixelRepresentation = 1; 1007 | break; 1008 | } 1009 | 1010 | result = frametoimage(buffer, 1, photometricInterpretation, samplesPerPixel, width, height, bitsAllocated, pixelRepresentation, ¶meters, &image); 1011 | 1012 | if(djrp->useLosslessProcess()) 1013 | { 1014 | parameters.tcp_numlayers = 1; 1015 | parameters.tcp_rates[0] = 0; 1016 | parameters.cp_disto_alloc = 1; 1017 | } 1018 | else 1019 | { 1020 | parameters.tcp_numlayers = 1; 1021 | if(djrp->getnearlosslessPSNR() == 0) 1022 | parameters.tcp_distoratio[0] = (bitsAllocated == 8)? 50 : 80; // 50 or 80db depending on 8 or 16 1023 | else 1024 | parameters.tcp_distoratio[0] = djrp->getnearlosslessPSNR(); 1025 | parameters.cp_fixed_quality = 1; 1026 | } 1027 | 1028 | if(djcp->getUseCustomOptions()) 1029 | { 1030 | parameters.cblockw_init = djcp->get_cblkwidth(); 1031 | parameters.cblockh_init = djcp->get_cblkheight(); 1032 | } 1033 | 1034 | // turn on/off MCT depending on transfer syntax 1035 | if(supportedTransferSyntax() == EXS_JPEG2000) 1036 | parameters.tcp_mct = 0; 1037 | else if(supportedTransferSyntax() == EXS_JPEG2000Multicomponent) 1038 | parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; 1039 | 1040 | 1041 | // We have no idea how big the compressed pixel data will be and we have no 1042 | // way to find out, so we just allocate a buffer large enough for the raw data 1043 | // plus a little more for JPEG metadata. 1044 | // Yes, this is way too much for just a little JPEG metadata, but some 1045 | // test-images showed that the buffer previously was too small. Plus, at some 1046 | // places charls fails to do proper bounds checking and writes behind the end 1047 | // of the buffer (sometimes way behind its end...). 1048 | size_t compressed_buffer_size = buffer_size + 1024; 1049 | Uint8 *compressed_buffer = new Uint8[compressed_buffer_size]; 1050 | 1051 | // Set up the information structure for OpenJPEG 1052 | opj_stream_t *l_stream = NULL; 1053 | opj_codec_t* l_codec = NULL; 1054 | l_codec = opj_create_compress(OPJ_CODEC_J2K); 1055 | 1056 | opj_set_info_handler(l_codec, msg_callback, NULL); 1057 | opj_set_warning_handler(l_codec, msg_callback, NULL); 1058 | opj_set_error_handler(l_codec, msg_callback, NULL); 1059 | 1060 | if (result.good() && !opj_setup_encoder(l_codec, ¶meters, image)) 1061 | { 1062 | opj_destroy_codec(l_codec); 1063 | result = EC_MemoryExhausted; 1064 | } 1065 | 1066 | DecodeData mysrc((unsigned char*)compressed_buffer, compressed_buffer_size); 1067 | l_stream = opj_stream_create_memory_stream(&mysrc, compressed_buffer_size, OPJ_FALSE); 1068 | 1069 | if(result.good() && !opj_start_compress(l_codec,image,l_stream)) 1070 | { 1071 | result = EC_CorruptedData; 1072 | } 1073 | 1074 | if(result.good() && !opj_encode(l_codec, l_stream)) 1075 | { 1076 | result = EC_InvalidStream; 1077 | } 1078 | 1079 | if(result.good() && opj_end_compress(l_codec, l_stream)) 1080 | { 1081 | result = EC_Normal; 1082 | } 1083 | 1084 | opj_stream_destroy(l_stream); 1085 | opj_destroy_codec(l_codec); 1086 | opj_image_destroy(image); 1087 | 1088 | compressed_buffer_size = mysrc.offset; 1089 | 1090 | if (result.good()) 1091 | { 1092 | // 'compressed_buffer_size' now contains the size of the compressed data in buffer 1093 | compressedSize = compressed_buffer_size; 1094 | result = pixelSequence->storeCompressedFrame(offsetList, compressed_buffer, compressed_buffer_size, fragmentSize); 1095 | } 1096 | 1097 | delete[] buffer; 1098 | delete[] compressed_buffer; 1099 | 1100 | 1101 | return result; 1102 | } 1103 | 1104 | OFCondition DJPEG2KEncoderBase::convertToUninterleaved( 1105 | Uint8 *target, 1106 | const Uint8 *source, 1107 | Uint16 components, 1108 | Uint32 width, 1109 | Uint32 height, 1110 | Uint16 bitsAllocated) const 1111 | { 1112 | Uint8 bytesAllocated = bitsAllocated / 8; 1113 | Uint32 planeSize = width * height * bytesAllocated; 1114 | 1115 | if (bitsAllocated % 8 != 0) 1116 | return EC_IllegalCall; 1117 | 1118 | for (Uint32 pos = 0; pos < width * height; pos++) 1119 | { 1120 | for (int i = 0; i < components; i++) 1121 | { 1122 | memcpy(&target[i * planeSize + pos * bytesAllocated], source, bytesAllocated); 1123 | source += bytesAllocated; 1124 | } 1125 | } 1126 | return EC_Normal; 1127 | } 1128 | 1129 | OFCondition frametoimage(const Uint8 *framePointer, int planarConfiguration, OFString photometricInterpretation, int samplesPerPixel, int width, int height, int bitsAllocated, OFBool isSigned, opj_cparameters_t *parameters, opj_image_t **ret_image) 1130 | { 1131 | Uint8 bytesAllocated = bitsAllocated / 8; 1132 | // Uint32 PixelWidth = bytesAllocated * samplesPerPixel; 1133 | 1134 | if (bitsAllocated % 8 != 0) 1135 | return EC_IllegalCall; 1136 | 1137 | opj_image_t *image = NULL; 1138 | opj_image_cmptparm_t *cmptparm = new opj_image_cmptparm_t[samplesPerPixel]; 1139 | memset(cmptparm, 0, samplesPerPixel * sizeof(opj_image_cmptparm_t)); 1140 | 1141 | int subsampling_dx = parameters->subsampling_dx; 1142 | int subsampling_dy = parameters->subsampling_dy; 1143 | 1144 | for (int i = 0; i < samplesPerPixel; i++) 1145 | { 1146 | cmptparm[i].prec = bitsAllocated; 1147 | cmptparm[i].bpp = bitsAllocated; 1148 | cmptparm[i].sgnd = (isSigned) ? 1 : 0; 1149 | cmptparm[i].dx = (OPJ_UINT32)subsampling_dx; 1150 | cmptparm[i].dy = (OPJ_UINT32)subsampling_dy; 1151 | cmptparm[i].w = width; 1152 | cmptparm[i].h = height; 1153 | } 1154 | 1155 | COLOR_SPACE colorspace = OPJ_CLRSPC_UNSPECIFIED; 1156 | if(photometricInterpretation == "MONOCHROME1") 1157 | colorspace = OPJ_CLRSPC_GRAY; 1158 | else if(photometricInterpretation == "MONOCHROME2") 1159 | colorspace = OPJ_CLRSPC_GRAY; 1160 | else if(photometricInterpretation == "RGB") 1161 | colorspace = OPJ_CLRSPC_SRGB; 1162 | else if(photometricInterpretation == "YBR_FULL") 1163 | colorspace = OPJ_CLRSPC_SYCC; 1164 | 1165 | image = opj_image_create(samplesPerPixel, cmptparm, colorspace); 1166 | 1167 | // set image offset and reference grid 1168 | image->x0 = (OPJ_UINT32)parameters->image_offset_x0; 1169 | image->y0 = (OPJ_UINT32)parameters->image_offset_y0; 1170 | image->x1 = !image->x0 ? (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1 : image->x0 + (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1; 1171 | image->y1 = !image->y0 ? (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1 : image->y0 + (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1; 1172 | 1173 | if(bytesAllocated == 1) 1174 | { 1175 | if(planarConfiguration == 0) 1176 | { 1177 | for(int i = 0; i < samplesPerPixel; i++) 1178 | { 1179 | OPJ_INT32 *target = image->comps[i].data; 1180 | const Uint8 *source = &framePointer[i * bytesAllocated]; 1181 | for (unsigned int pos = 0; pos < height * width; pos++) 1182 | { 1183 | *target = (isSigned) ? (Sint8) *source : *source; 1184 | target++; 1185 | source += samplesPerPixel; 1186 | } 1187 | } 1188 | } 1189 | else 1190 | { 1191 | for(int i = 0; i < samplesPerPixel; i++) 1192 | { 1193 | OPJ_INT32 *target = image->comps[i].data; 1194 | const Uint8 *source = &framePointer[i * height * width * bytesAllocated]; 1195 | for (unsigned int pos = 0; pos < height * width; pos++) 1196 | { 1197 | *target = (isSigned) ? (Sint8) *source : *source; 1198 | target++; 1199 | source++; 1200 | } 1201 | } 1202 | } 1203 | } 1204 | else if(bytesAllocated == 2) 1205 | { 1206 | if(planarConfiguration == 0) 1207 | { 1208 | for(int i = 0; i < samplesPerPixel; i++) 1209 | { 1210 | OPJ_INT32 *target = image->comps[i].data; 1211 | const Uint16 *source = (Uint16 *) &framePointer[i * bytesAllocated]; 1212 | for (unsigned int pos = 0; pos < height * width; pos++) 1213 | { 1214 | *target = (isSigned) ? (Sint16) *source : *source; 1215 | target++; 1216 | source += samplesPerPixel; 1217 | } 1218 | } 1219 | } 1220 | else 1221 | { 1222 | for(int i = 0; i < samplesPerPixel; i++) 1223 | { 1224 | OPJ_INT32 *target = image->comps[i].data; 1225 | const Uint16 *source = (Uint16 *) &framePointer[i * height * width * bytesAllocated]; 1226 | for (unsigned int pos = 0; pos < height * width; pos++) 1227 | { 1228 | *target = (isSigned) ? (Sint16) *source : *source; 1229 | target++; 1230 | source++; 1231 | } 1232 | } 1233 | } 1234 | } 1235 | else if(bytesAllocated == 4) 1236 | { 1237 | if(planarConfiguration == 0) 1238 | { 1239 | for(int i = 0; i < samplesPerPixel; i++) 1240 | { 1241 | OPJ_INT32 *target = image->comps[i].data; 1242 | const Uint32 *source = (Uint32 *) &framePointer[i * bytesAllocated]; 1243 | for (unsigned int pos = 0; pos < height * width; pos++) 1244 | { 1245 | *target = (isSigned) ? (Sint32) *source : *source; 1246 | target++; 1247 | source += samplesPerPixel; 1248 | } 1249 | } 1250 | } 1251 | else 1252 | { 1253 | for(int i = 0; i < samplesPerPixel; i++) 1254 | { 1255 | OPJ_INT32 *target = image->comps[i].data; 1256 | const Uint32 *source = (Uint32 *) &framePointer[i * height * width * bytesAllocated]; 1257 | for (unsigned int pos = 0; pos < height * width; pos++) 1258 | { 1259 | *target = (isSigned) ? (Sint32) *source : *source; 1260 | target++; 1261 | source++; 1262 | } 1263 | } 1264 | } 1265 | } 1266 | delete [] cmptparm; 1267 | 1268 | *ret_image = image; 1269 | return EC_Normal; 1270 | } 1271 | /* 1272 | opj_image_t *frameToImage2(const Uint8 *framePointer, int width, int height, opj_cparameters_t *parameters) 1273 | { 1274 | opj_image_t *image = NULL; 1275 | opj_image_cmptparm_t cmptparm[1]; 1276 | memset(&cmptparm[0], 0, 1 * sizeof(opj_image_cmptparm_t)); 1277 | 1278 | int subsampling_dx = parameters->subsampling_dx; 1279 | int subsampling_dy = parameters->subsampling_dy; 1280 | 1281 | for (int i = 0; i < 1; i++) 1282 | { 1283 | cmptparm[i].prec = 16; 1284 | cmptparm[i].bpp = 16; 1285 | cmptparm[i].sgnd = 0; 1286 | cmptparm[i].dx = (OPJ_UINT32)subsampling_dx; 1287 | cmptparm[i].dy = (OPJ_UINT32)subsampling_dy; 1288 | cmptparm[i].w = width; 1289 | cmptparm[i].h = height; 1290 | } 1291 | 1292 | image = opj_image_create(1, &cmptparm[0], OPJ_CLRSPC_SRGB); 1293 | 1294 | // set image offset and reference grid 1295 | image->x0 = (OPJ_UINT32)parameters->image_offset_x0; 1296 | image->y0 = (OPJ_UINT32)parameters->image_offset_y0; 1297 | image->x1 = !image->x0 ? (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1 : image->x0 + (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1; 1298 | image->y1 = !image->y0 ? (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1 : image->y0 + (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1; 1299 | 1300 | Uint16 *ptr = (Uint16 *)framePointer; 1301 | int index = 0; 1302 | for (int y = 0; y < height; y++) 1303 | { 1304 | for (int x = 0; x < width; x++) 1305 | { 1306 | image->comps[0].data[index] = *ptr; 1307 | index++; 1308 | ptr++; 1309 | } 1310 | } 1311 | return image; 1312 | } 1313 | 1314 | opj_image_t *frameToImage3(const Uint8 *framePointer, int width, int height, opj_cparameters_t *parameters) 1315 | { 1316 | opj_image_t *image = NULL; 1317 | opj_image_cmptparm_t cmptparm[3]; 1318 | memset(&cmptparm[0], 0, 3 * sizeof(opj_image_cmptparm_t)); 1319 | 1320 | int subsampling_dx = parameters->subsampling_dx; 1321 | int subsampling_dy = parameters->subsampling_dy; 1322 | 1323 | for (int i = 0; i < 3; i++) 1324 | { 1325 | cmptparm[i].prec = 8; 1326 | cmptparm[i].bpp = 8; 1327 | cmptparm[i].sgnd = 0; 1328 | cmptparm[i].dx = (OPJ_UINT32)subsampling_dx; 1329 | cmptparm[i].dy = (OPJ_UINT32)subsampling_dy; 1330 | cmptparm[i].w = width; 1331 | cmptparm[i].h = height; 1332 | } 1333 | 1334 | image = opj_image_create(3, &cmptparm[0], OPJ_CLRSPC_SRGB); 1335 | 1336 | if(image != NULL) 1337 | { 1338 | // set image offset and reference grid 1339 | image->x0 = (OPJ_UINT32)parameters->image_offset_x0; 1340 | image->y0 = (OPJ_UINT32)parameters->image_offset_y0; 1341 | image->x1 = !image->x0 ? (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1 : image->x0 + (OPJ_UINT32)(width - 1) * (OPJ_UINT32)subsampling_dx + 1; 1342 | image->y1 = !image->y0 ? (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1 : image->y0 + (OPJ_UINT32)(height - 1) * (OPJ_UINT32)subsampling_dy + 1; 1343 | 1344 | Uint8 *ptr = (Uint8 *)framePointer; 1345 | int index = 0; 1346 | for (int y = 0; y < height; y++) 1347 | { 1348 | for (int x = 0; x < width; x++) 1349 | { 1350 | image->comps[0].data[index] = *ptr; 1351 | image->comps[1].data[index] = *ptr; 1352 | image->comps[2].data[index] = *ptr; 1353 | index++; 1354 | ptr++; 1355 | } 1356 | } 1357 | } 1358 | return image; 1359 | } 1360 | 1361 | */ --------------------------------------------------------------------------------