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()); }
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"; } } }
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); } }
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); } }
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); } }
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)); } }
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); }
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); }
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); }
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(); }
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; }
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); } }