view Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp @ 1013:53cc787bd7bc toa2019092301

- Added an optimized ProjectPoint2 to CoordinateSystem3D. It has *not* replaced the ProjectPoint method because more tests need to be written. - ProjectPointOntoPlane2 is a faster version of GeometryToolbox::ProjectPointOntoPlane. Same remark as above. - DicomStructureSet.cpp now uses this optimized call.
author Benjamin Golinvaux <bgo@osimis.io>
date Mon, 23 Sep 2019 15:18:33 +0200
parents a7351ad54960
children 81b29bc7c3d4 2d8ab34c8c91
line wrap: on
line source

/**
 * Stone of Orthanc
 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
 * Department, University Hospital of Liege, Belgium
 * Copyright (C) 2017-2019 Osimis S.A., Belgium
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 **/


#include "GetOrthancWebViewerJpegCommand.h"

#include "../Toolbox/LinearAlgebra.h"

#include <Core/Images/Image.h>
#include <Core/Images/ImageProcessing.h>
#include <Core/Images/JpegReader.h>
#include <Core/OrthancException.h>
#include <Core/Toolbox.h>

#include <json/reader.h>
#include <json/value.h>

namespace OrthancStone
{
  GetOrthancWebViewerJpegCommand::SuccessMessage::SuccessMessage(const GetOrthancWebViewerJpegCommand& command,
                                                                 Orthanc::ImageAccessor* image) :   // Takes ownership
    OriginMessage(command),
    image_(image)
  {
    if (image == NULL)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
    }
  }


  GetOrthancWebViewerJpegCommand::GetOrthancWebViewerJpegCommand() :
    frame_(0),
    quality_(95),
    timeout_(600),
    expectedFormat_(Orthanc::PixelFormat_Grayscale8)
  {
  }


  void GetOrthancWebViewerJpegCommand::SetQuality(unsigned int quality)
  {
    if (quality <= 0 ||
        quality > 100)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
    }
    else
    {
      quality_ = quality;
    }
  }


  std::string GetOrthancWebViewerJpegCommand::GetUri() const
  {
    return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) +
            "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_));
  }


  void GetOrthancWebViewerJpegCommand::ProcessHttpAnswer(IMessageEmitter& emitter,
                                                         const IObserver& receiver,
                                                         const std::string& answer) const
  {
    // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()"
      
    Json::Value encoded;

    {
      Json::Reader reader;
      if (!reader.parse(answer, encoded))
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
      }
    }

    if (encoded.type() != Json::objectValue ||
        !encoded.isMember("Orthanc") ||
        encoded["Orthanc"].type() != Json::objectValue)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
    }
    
    const Json::Value& info = encoded["Orthanc"];
    if (!info.isMember("PixelData") ||
        !info.isMember("Stretched") ||
        !info.isMember("Compression") ||
        info["Compression"].type() != Json::stringValue ||
        info["PixelData"].type() != Json::stringValue ||
        info["Stretched"].type() != Json::booleanValue ||
        info["Compression"].asString() != "Jpeg")
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
    }
    
    bool isSigned = false;
    bool isStretched = info["Stretched"].asBool();
    
    if (info.isMember("IsSigned"))
    {
      if (info["IsSigned"].type() != Json::booleanValue)
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
      }
      else
      {
        isSigned = info["IsSigned"].asBool();
      }
    }
    
    std::auto_ptr<Orthanc::ImageAccessor> reader;
    
    {
      std::string jpeg;
      Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
      
      reader.reset(new Orthanc::JpegReader);
      dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
    }
    
    if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
    {
      if (expectedFormat_ != Orthanc::PixelFormat_RGB24)
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
      }
      
      if (isSigned || isStretched)
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
      }
      else
      {
        SuccessMessage message(*this, reader.release());
        emitter.EmitMessage(receiver, message);
        return;
      }
    }
    
    if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
    }
    
    if (!isStretched)
    {
      if (expectedFormat_ != reader->GetFormat())
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
      }
      else
      {
        SuccessMessage message(*this, reader.release());
        emitter.EmitMessage(receiver, message);
        return;
      }
    }
    
    int32_t stretchLow = 0;
    int32_t stretchHigh = 0;
    
    if (!info.isMember("StretchLow") ||
        !info.isMember("StretchHigh") ||
        info["StretchLow"].type() != Json::intValue ||
        info["StretchHigh"].type() != Json::intValue)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
    }
    
    stretchLow = info["StretchLow"].asInt();
    stretchHigh = info["StretchHigh"].asInt();
    
    if (stretchLow < -32768 ||
        stretchHigh > 65535 ||
        (stretchLow < 0 && stretchHigh > 32767))
    {
      // This range cannot be represented with a uint16_t or an int16_t
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
    }
    
    // Decode a grayscale JPEG 8bpp image coming from the Web viewer
    std::auto_ptr<Orthanc::ImageAccessor> image
      (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false));

    Orthanc::ImageProcessing::Convert(*image, *reader);
    reader.reset();
    
    float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
    
    if (!LinearAlgebra::IsCloseToZero(scaling))
    {
      float offset = static_cast<float>(stretchLow) / scaling;
      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
    }
    
    SuccessMessage message(*this, image.release());
    emitter.EmitMessage(receiver, message);
  }
}