Skip to content

Commit

Permalink
feat: add retrieve thumbnail APIs
Browse files Browse the repository at this point in the history
- Add `ThumbnailService` to handle getting thumbnail and do response
- `ThumbnailFactory` to get thumbnail instance
- Return median instance's median frame
> models/mongodb/models/dicomSeries.js
> models/mongodb/models/dicom.js
- Add every level's thumbnail's API
  • Loading branch information
Chinlinlee committed May 2, 2023
1 parent b5adfbf commit 3bcc342
Show file tree
Hide file tree
Showing 10 changed files with 663 additions and 2 deletions.
172 changes: 172 additions & 0 deletions api/dicom-web/controller/WADO-RS/service/thumbnail.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
const dicomModel = require("../../../../../models/mongodb/models/dicom");
const dicomSeriesModel = require("../../../../../models/mongodb/models/dicomSeries");
const errorResponse = require("../../../../../utils/errorResponse/errorResponseMessage");
const renderedService = require("../service/rendered.service");
const _ = require("lodash");
class ThumbnailService {

/**
*
* @param {import("express").Request} req
* @param {import("express").Response} res
* @param {typeof ThumbnailFactory} thumbnailFactory
*/
constructor(req, res, apiLogger, thumbnailFactory) {
this.request = req;
this.response = res;
this.thumbnailFactory = new thumbnailFactory(this.request.params);
this.apiLogger = apiLogger;
}

async getThumbnailAndResponse() {
if (!_.get(this.request, "query.viewport")) {
_.set(this.request, "query.viewport", "100,100");
}

let instanceFramesObj = await this.thumbnailFactory.getThumbnailInstance();
if (this.checkInstanceExists(instanceFramesObj)) {
return;
}

let thumbnail = await this.getThumbnailByInstance(instanceFramesObj);
if (thumbnail) {
return this.response.end(thumbnail, "binary");
}
throw new Error(`Can not process this image, instanceUID: ${instanceFramesObj.instanceUID}`);
}

async getThumbnailByInstance(instanceFramesObj) {
let dicomNumberOfFrames = _.get(instanceFramesObj, "00280008.Value.0", 1);
dicomNumberOfFrames = parseInt(dicomNumberOfFrames);
let medianFrame = 1;
if (dicomNumberOfFrames > 1) medianFrame = dicomNumberOfFrames >> 1;
if (this.request.params.frameNumber) {
medianFrame = this.request.params.frameNumber[0];
}

let postProcessResult = await renderedService.postProcessFrameImage(this.request, medianFrame, instanceFramesObj);
if (postProcessResult.status) {
this.response.writeHead(200, {
"Content-Type": "image/jpeg"
});
this.apiLogger.logger.info(`Get instance's thumbnail successfully, instance UID: ${instanceFramesObj.instanceUID}`);
return postProcessResult.magick.toBuffer();
}
return undefined;
}

checkInstanceExists(instanceFramesObj) {
if (!instanceFramesObj) {
this.response.writeHead(404, {
"Content-Type": "application/dicom+json"
});
let notFoundMessage = errorResponse.getNotFoundErrorMessage(`Not Found, ${this.thumbnailFactory.getUidsString()}`);

let notFoundMessageStr = JSON.stringify(notFoundMessage);

this.apiLogger.logger.warn(`[${notFoundMessageStr}]`);

return this.response.end(notFoundMessageStr);
}
return undefined;
}
}

class ThumbnailFactory {
/**
*
* @param {import("../../../../../utils/typeDef/dicom").Uids} uids
*/
constructor(uids) {
this.uids = uids;
}

async getThumbnailInstance() { }

getUidsString() {
let uidsKeys = Object.keys(this.uids);
let strArr = [];
for (let i = 0; i < uidsKeys.length; i++) {
let key = uidsKeys[i];
strArr.push(`${key}: ${this.uids[key]}`);
}
return strArr.join(", ");
}
}

class StudyThumbnailFactory extends ThumbnailFactory {
constructor(uids) {
super(uids);
}

/**
*
* @param {import("../../../../../utils/typeDef/dicom").Uids} uids
*/
async getThumbnailInstance() {
let medianSeries = await dicomSeriesModel.getSeriesOfMedianIndex(this.uids.studyUID);
if (!medianSeries) return undefined;

let medianInstance = await dicomModel.getInstanceOfMedianIndex(this.uids.studyUID, medianSeries.seriesUID);
if (!medianInstance) return undefined;

let instanceFramesObj = await renderedService.getInstanceFrameObj({
studyUID: this.uids.studyUID,
seriesUID: medianSeries.seriesUID,
instanceUID: medianInstance.instanceUID
});

return instanceFramesObj;
}

}

class SeriesThumbnailFactory extends ThumbnailFactory {
constructor(uids) {
super(uids);
}

/**
*
* @param {import("../../../../../utils/typeDef/dicom").Uids} uids
*/
async getThumbnailInstance() {
let medianInstance = await dicomModel.getInstanceOfMedianIndex(this.uids.studyUID, this.uids.seriesUID);
if (!medianInstance) return undefined;

let instanceFramesObj = await renderedService.getInstanceFrameObj({
studyUID: this.uids.studyUID,
seriesUID: this.uids.seriesUID,
instanceUID: medianInstance.instanceUID
});

return instanceFramesObj;
}

}

class InstanceThumbnailFactory extends ThumbnailFactory {
constructor(uids) {
super(uids);
}

/**
*
* @param {import("../../../../../utils/typeDef/dicom").Uids} uids
*/
async getThumbnailInstance() {
let instanceFramesObj = await renderedService.getInstanceFrameObj({
studyUID: this.uids.studyUID,
seriesUID: this.uids.seriesUID,
instanceUID: this.uids.instanceUID
});

return instanceFramesObj;
}

}

module.exports.ThumbnailService = ThumbnailService;
module.exports.StudyThumbnailFactory = StudyThumbnailFactory;
module.exports.SeriesThumbnailFactory = SeriesThumbnailFactory;
module.exports.InstanceThumbnailFactory = InstanceThumbnailFactory;
52 changes: 52 additions & 0 deletions api/dicom-web/controller/WADO-RS/thumbnail/frame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { Controller } = require("../../../../controller.class");
const { ApiLogger } = require("../../../../../utils/logs/api-logger");
const {
ThumbnailService,
InstanceThumbnailFactory
} = require("../service/thumbnail.service");



class RetrieveFrameThumbnailController extends Controller {
constructor(req, res) {
super(req, res);
}

async mainProcess() {

let apiLogger = new ApiLogger(this.request, "WADO-RS");
apiLogger.addTokenValue();

apiLogger.logger.info(`Get Study's Series' Instance Thumbnail [study UID: ${this.request.params.studyUID},\
series UID: ${this.request.params.seriesUID}]\
instance UID: ${this.request.params.instanceUID}\
frames: ${JSON.stringify(this.request.params.frameNumber)}`);

try {
let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, InstanceThumbnailFactory);
return thumbnailService.getThumbnailAndResponse();
} catch (e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
apiLogger.logger.error(errorStr);

this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
return this.response.end({
code: 500,
message: "An exception occurred"
});
}
}
}

/**
*
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
module.exports = async function (req, res) {
let controller = new RetrieveFrameThumbnailController(req, res);

await controller.doPipeline();
};
51 changes: 51 additions & 0 deletions api/dicom-web/controller/WADO-RS/thumbnail/instance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { Controller } = require("../../../../controller.class");
const { ApiLogger } = require("../../../../../utils/logs/api-logger");
const {
ThumbnailService,
InstanceThumbnailFactory
} = require("../service/thumbnail.service");



class RetrieveInstanceThumbnailController extends Controller {
constructor(req, res) {
super(req, res);
}

async mainProcess() {

let apiLogger = new ApiLogger(this.request, "WADO-RS");
apiLogger.addTokenValue();

apiLogger.logger.info(`Get Study's Series' Instance Thumbnail [study UID: ${this.request.params.studyUID},\
series UID: ${this.request.params.seriesUID}]\
instance UID: ${this.request.params.instanceUID}`);

try {
let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, InstanceThumbnailFactory);
return thumbnailService.getThumbnailAndResponse();
} catch (e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
apiLogger.logger.error(errorStr);

this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
return this.response.end({
code: 500,
message: "An exception occurred"
});
}
}
}

/**
*
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
module.exports = async function (req, res) {
let controller = new RetrieveInstanceThumbnailController(req, res);

await controller.doPipeline();
};
50 changes: 50 additions & 0 deletions api/dicom-web/controller/WADO-RS/thumbnail/series.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { Controller } = require("../../../../controller.class");
const { ApiLogger } = require("../../../../../utils/logs/api-logger");
const {
ThumbnailService,
SeriesThumbnailFactory
} = require("../service/thumbnail.service");



class RetrieveSeriesThumbnailController extends Controller {
constructor(req, res) {
super(req, res);
}

async mainProcess() {

let apiLogger = new ApiLogger(this.request, "WADO-RS");
apiLogger.addTokenValue();

apiLogger.logger.info(`Get Study's Series' Thumbnail [study UID: ${this.request.params.studyUID},\
series UID: ${this.request.params.seriesUID}]`);

try {
let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, SeriesThumbnailFactory);
return thumbnailService.getThumbnailAndResponse();
} catch (e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
apiLogger.logger.error(errorStr);

this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
return this.response.end({
code: 500,
message: "An exception occurred"
});
}
}
}

/**
*
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
module.exports = async function (req, res) {
let controller = new RetrieveSeriesThumbnailController(req, res);

await controller.doPipeline();
};
49 changes: 49 additions & 0 deletions api/dicom-web/controller/WADO-RS/thumbnail/study.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { Controller } = require("../../../../controller.class");
const { ApiLogger } = require("../../../../../utils/logs/api-logger");
const {
ThumbnailService,
StudyThumbnailFactory
} = require("../service/thumbnail.service");



class RetrieveStudyThumbnailController extends Controller {
constructor(req, res) {
super(req, res);
}

async mainProcess() {

let apiLogger = new ApiLogger(this.request, "WADO-RS");
apiLogger.addTokenValue();

apiLogger.logger.info(`Get Study's Thumbnail [study UID: ${this.request.params.studyUID}]`);

try {
let thumbnailService = new ThumbnailService(this.request, this.response, apiLogger, StudyThumbnailFactory);
return thumbnailService.getThumbnailAndResponse();
} catch (e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
apiLogger.logger.error(errorStr);

this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
return this.response.end({
code: 500,
message: "An exception occurred"
});
}
}
}

/**
*
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
module.exports = async function (req, res) {
let controller = new RetrieveStudyThumbnailController(req, res);

await controller.doPipeline();
};
Loading

0 comments on commit 3bcc342

Please sign in to comment.