コード例 #1
0
ファイル: DicomMapTests.cpp プロジェクト: dhanzhang/orthanc
TEST(DicomMap, Tags)
{
  DicomMap m;
  ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_NAME));
  ASSERT_FALSE(m.HasTag(0x0010, 0x0010));
  m.SetValue(0x0010, 0x0010, "PatientName");
  ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_NAME));
  ASSERT_TRUE(m.HasTag(0x0010, 0x0010));

  ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_ID));
  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID");
  ASSERT_TRUE(m.HasTag(0x0010, 0x0020));
  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2");
  ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).AsString());

  m.Remove(DICOM_TAG_PATIENT_ID);
  ASSERT_THROW(m.GetValue(0x0010, 0x0020), OrthancException);

  std::auto_ptr<DicomMap> mm(m.Clone());
  ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).AsString());  

  m.SetValue(DICOM_TAG_PATIENT_ID, "Hello");
  ASSERT_THROW(mm->GetValue(DICOM_TAG_PATIENT_ID), OrthancException);
  mm->CopyTagIfExists(m, DICOM_TAG_PATIENT_ID);
  ASSERT_EQ("Hello", mm->GetValue(DICOM_TAG_PATIENT_ID).AsString());  

  DicomNullValue v;
  ASSERT_TRUE(v.IsNull());
}
コード例 #2
0
  static void ComputeStudyCounters(DicomMap& result,
                                   ServerContext& context,
                                   const std::string& study,
                                   const DicomMap& query)
  {
    ServerIndex& index = context.GetIndex();

    std::list<std::string> series;
    index.GetChildren(series, study);
    
    if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES,
                      boost::lexical_cast<std::string>(series.size()), false);
    }

    if (query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY))
    {
      std::set<std::string> values;
      ExtractTagFromMainDicomTags(values, index, DICOM_TAG_MODALITY, series, ResourceType_Series);
      StoreSetOfStrings(result, DICOM_TAG_MODALITIES_IN_STUDY, values);
    }

    if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) &&
        !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY))
    {
      return;
    }

    std::list<std::string> instances;
    GetChildren(instances, index, series);

    if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES,
                      boost::lexical_cast<std::string>(instances.size()), false);
    }

    if (query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY))
    {
      if (Configuration::GetGlobalBoolParameter("AllowFindSopClassesInStudy", false))
      {
        std::set<std::string> values;
        ExtractTagFromInstances(values, context, DICOM_TAG_SOP_CLASS_UID, instances);
        StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values);
      }
      else
      {
        result.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, "", false);
        LOG(WARNING) << "The handling of \"SOP Classes in Study\" (0008,0062) "
                     << "in C-FIND requests is disabled";
      }
    }
  }
コード例 #3
0
  static void ComputePatientCounters(DicomMap& result,
                                     ServerIndex& index,
                                     const std::string& patient,
                                     const DicomMap& query)
  {
    std::list<std::string> studies;
    index.GetChildren(studies, patient);

    if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES,
                      boost::lexical_cast<std::string>(studies.size()), false);
    }

    if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) &&
        !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
    {
      return;
    }

    std::list<std::string> series;
    GetChildren(series, index, studies);
    studies.clear();  // This information is useless below
    
    if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES,
                      boost::lexical_cast<std::string>(series.size()), false);
    }

    if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
    {
      return;
    }

    std::list<std::string> instances;
    GetChildren(instances, index, series);

    if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES,
                      boost::lexical_cast<std::string>(instances.size()), false);
    }
  }
コード例 #4
0
  static void AddAnswer(DicomFindAnswers& answers,
                        const Json::Value& resource,
                        const DicomArray& query)
  {
    DicomMap result;

    for (size_t i = 0; i < query.GetSize(); i++)
    {
      // Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052))
      if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL)
      {
        result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue());
      }
      else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET)
      {
      }
      else
      {
        std::string tag = query.GetElement(i).GetTag().Format();
        std::string value;
        if (resource.isMember(tag))
        {
          value = resource.get(tag, Json::arrayValue).get("Value", "").asString();
          result.SetValue(query.GetElement(i).GetTag(), value);
        }
        else
        {
          result.SetValue(query.GetElement(i).GetTag(), "");
        }
      }
    }

    if (result.GetSize() == 0)
    {
      LOG(WARNING) << "The C-FIND request does not return any DICOM tag";
    }
    else
    {
      answers.Add(result);
    }
  }
コード例 #5
0
  static void ComputeSeriesCounters(DicomMap& result,
                                    ServerIndex& index,
                                    const std::string& series,
                                    const DicomMap& query)
  {
    std::list<std::string> instances;
    index.GetChildren(instances, series);

    if (query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES))
    {
      result.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES,
                      boost::lexical_cast<std::string>(instances.size()), false);
    }
  }
コード例 #6
0
void DatabaseWrapperBase::GetMainDicomTags(DicomMap& map,
        int64_t id)
{
    map.Clear();

    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?");
    s.BindInt64(0, id);
    while (s.Step())
    {
        map.SetValue(s.ColumnInt(1),
                     s.ColumnInt(2),
                     s.ColumnString(3));
    }
}
コード例 #7
0
TEST(DicomImageInformation, ExtractPixelFormat2)
{
  // Delphine CT
  DicomMap m;
  m.SetValue(DICOM_TAG_ROWS, "24");
  m.SetValue(DICOM_TAG_COLUMNS, "16");
  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
  m.SetValue(DICOM_TAG_BITS_STORED, "16");
  m.SetValue(DICOM_TAG_HIGH_BIT, "15");
  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1");
  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");

  DicomImageInformation info(m);
  PixelFormat format;
  ASSERT_TRUE(info.ExtractPixelFormat(format));
  ASSERT_EQ(PixelFormat_SignedGrayscale16, format);
}
コード例 #8
0
TEST(DicomImageInformation, ExtractPixelFormat1)
{
  // Cardiac/MR*
  DicomMap m;
  m.SetValue(DICOM_TAG_ROWS, "24", false);
  m.SetValue(DICOM_TAG_COLUMNS, "16", false);
  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false);
  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false);
  m.SetValue(DICOM_TAG_BITS_STORED, "12", false);
  m.SetValue(DICOM_TAG_HIGH_BIT, "11", false);
  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0", false);
  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false);

  DicomImageInformation info(m);
  PixelFormat format;
  ASSERT_TRUE(info.ExtractPixelFormat(format, false));
  ASSERT_EQ(PixelFormat_Grayscale16, format);
}
コード例 #9
0
  static void StoreSetOfStrings(DicomMap& result,
                                const DicomTag& tag,
                                const std::set<std::string>& values)
  {
    bool isFirst = true;

    std::string s;
    for (std::set<std::string>::const_iterator
           it = values.begin(); it != values.end(); ++it)
    {
      if (isFirst)
      {
        isFirst = false;
      }
      else
      {
        s += "\\";
      }

      s += *it;
    }

    result.SetValue(tag, s, false);
  }
コード例 #10
0
TEST(ServerIndex, AttachmentRecycling)
{
  const std::string path = "UnitTestsStorage";

  Toolbox::RemoveFile(path + "/index");
  FilesystemStorage storage(path);
  DatabaseWrapper db;   // The SQLite DB is in memory
  ServerContext context(db);
  context.SetStorageArea(storage);
  ServerIndex& index = context.GetIndex();

  index.SetMaximumStorageSize(10);

  Json::Value tmp;
  index.ComputeStatistics(tmp);
  ASSERT_EQ(0, tmp["CountPatients"].asInt());
  ASSERT_EQ(0, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString()));

  ServerIndex::Attachments attachments;

  std::vector<std::string> ids;
  for (int i = 0; i < 10; i++)
  {
    std::string id = boost::lexical_cast<std::string>(i);
    DicomMap instance;
    instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id);
    instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id);
    instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id);
    instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id);

    std::map<MetadataType, std::string> instanceMetadata;
    ServerIndex::MetadataMap metadata;
    ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, instance, attachments, "", metadata));
    ASSERT_EQ(2, instanceMetadata.size());
    ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_RemoteAet) != instanceMetadata.end());
    ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_ReceptionDate) != instanceMetadata.end());

    DicomInstanceHasher hasher(instance);
    ids.push_back(hasher.HashPatient());
    ids.push_back(hasher.HashStudy());
    ids.push_back(hasher.HashSeries());
    ids.push_back(hasher.HashInstance());
  }

  index.ComputeStatistics(tmp);
  ASSERT_EQ(10, tmp["CountPatients"].asInt());
  ASSERT_EQ(0, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString()));

  for (size_t i = 0; i < ids.size(); i++)
  {
    FileInfo info(Toolbox::GenerateUuid(), FileContentType_Dicom, 1, "md5");
    index.AddAttachment(info, ids[i]);

    index.ComputeStatistics(tmp);
    ASSERT_GE(10, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString()));
  }

  // Because the DB is in memory, the SQLite index must not have been created
  ASSERT_THROW(Toolbox::GetFileSize(path + "/index"), OrthancException);  

  context.Stop();
}
コード例 #11
0
  bool OrthancFindRequestHandler::ApplyLuaFilter(DicomMap& target,
                                                 const DicomMap& source,
                                                 const std::string& remoteIp,
                                                 const std::string& remoteAet,
                                                 const std::string& calledAet)
  {
    Json::Value output;

    {
      LuaScripting::Locker locker(context_.GetLua());
      static const char* NAME = "IncomingFindRequestFilter";
      
      if (!locker.GetLua().IsExistingFunction(NAME))
      {
        return false;
      }

      Json::Value tmp = Json::objectValue;
      DicomArray a(source);

      for (size_t i = 0; i < a.GetSize(); i++)
      {
        const DicomValue& v = a.GetElement(i).GetValue();
        std::string s = (v.IsNull() || v.IsBinary()) ? "" : v.GetContent();
        tmp[a.GetElement(i).GetTag().Format()] = s;
      }

      Json::Value origin = Json::objectValue;
      origin["RemoteIp"] = remoteIp;
      origin["RemoteAet"] = remoteAet;
      origin["CalledAet"] = calledAet;

      LuaFunctionCall call(locker.GetLua(), NAME);
      call.PushJson(tmp);
      call.PushJson(origin);

      call.ExecuteToJson(output, true);
    }

    // The Lua context is released at this point

    if (output.type() != Json::objectValue)
    {
      LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table";
      throw OrthancException(ErrorCode_LuaBadOutput);
    }

    Json::Value::Members members = output.getMemberNames();

    for (size_t i = 0; i < members.size(); i++)
    {
      if (output[members[i]].type() != Json::stringValue)
      {
        LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings";
        throw OrthancException(ErrorCode_LuaBadOutput);
      }

      DicomTag tag(FromDcmtkBridge::ParseTag(members[i]));
      target.SetValue(tag, output[members[i]].asString(), false);
    }

    return true;
  }
コード例 #12
0
  static void AddAnswer(DicomFindAnswers& answers,
                        const Json::Value& resource,
                        const DicomArray& query,
                        const std::list<DicomTag>& sequencesToReturn,
                        const DicomMap* counters)
  {
    DicomMap result;

    for (size_t i = 0; i < query.GetSize(); i++)
    {
      if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL)
      {
        // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052))
        result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue());
      }
      else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET)
      {
        // Do not include the encoding, this is handled by class ParsedDicomFile
      }
      else
      {
        std::string tag = query.GetElement(i).GetTag().Format();
        std::string value;
        if (resource.isMember(tag))
        {
          value = resource.get(tag, Json::arrayValue).get("Value", "").asString();
          result.SetValue(query.GetElement(i).GetTag(), value, false);
        }
        else
        {
          result.SetValue(query.GetElement(i).GetTag(), "", false);
        }
      }
    }

    if (counters != NULL)
    {
      DicomArray tmp(*counters);
      for (size_t i = 0; i < tmp.GetSize(); i++)
      {
        result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent(), false);
      }
    }

    if (result.GetSize() == 0 &&
        sequencesToReturn.empty())
    {
      LOG(WARNING) << "The C-FIND request does not return any DICOM tag";
    }
    else if (sequencesToReturn.empty())
    {
      answers.Add(result);
    }
    else
    {
      ParsedDicomFile dicom(result);

      for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin();
           tag != sequencesToReturn.end(); ++tag)
      {
        const Json::Value& source = resource[tag->Format()];

        if (source.type() == Json::objectValue &&
            source.isMember("Type") &&
            source.isMember("Value") &&
            source["Type"].asString() == "Sequence" &&
            source["Value"].type() == Json::arrayValue)
        {
          Json::Value content = Json::arrayValue;

          for (Json::Value::ArrayIndex i = 0; i < source["Value"].size(); i++)
          {
            Json::Value item;
            Toolbox::SimplifyTags(item, source["Value"][i], DicomToJsonFormat_Short);
            content.append(item);
          }

          dicom.Replace(*tag, content, false, DicomReplaceMode_InsertIfAbsent);
        }
      }

      answers.Add(dicom);
    }
  }