Example #1
0
def _convert_to_png_and_store(_slice: Slice, slice_pixels: np.ndarray) -> None:
    """Convert given Slice's pixel array and store in databases.

    :param _slice: Slice database object
    :param slice_pixels: numpy array with Slice data
    """
    converted_image = _convert_slice_pixels_to_png(slice_pixels)
    SlicesRepository.store_converted_image(_slice.id, converted_image)
    _slice.update_status(SliceStatus.PROCESSED)
    logger.info('%s converted and stored.', _slice)
Example #2
0
def add_new_slice(scan_id: ScanID, image: bytes) -> Slice:
    """Add new Slice for given Scan.

    :param scan_id: ID of a Scan for which it should add new slice
    :param image: bytes representing Dicom image
    :return: Slice object
    """
    scan = ScansRepository.get_scan_by_id(scan_id)
    _slice = scan.add_slice()
    SlicesRepository.store_original_image(_slice.id, image)
    parse_dicom_and_update_slice.delay(_slice.id)
    return _slice
Example #3
0
def parse_dicom_and_update_slice(slice_id: SliceID) -> None:
    """Parse DICOM from Storage and update Slice for location and position.

    :param slice_id: ID of a slice
    """
    logger.debug('Parsing DICOM file from Storage for given Slice ID: %s.',
                 slice_id)
    _slice = SlicesRepository.get_slice_by_id(slice_id)
    image = SlicesRepository.get_slice_original_image(_slice.id)

    # We've got to store above DICOM image bytes on disk due to the fact that SimpleITK does not support
    # reading files from memory. It has to work on a file stored on a hard drive.
    temp_file = NamedTemporaryFile(delete=False)
    temp_file.write(image)
    temp_file.close()

    try:
        reader = sitk.ImageFileReader()
        reader.SetFileName(temp_file.name)
        reader.ReadImageInformation()

        location = SliceLocation(
            read_float(reader, DicomTag.SLICE_LOCATION) or 0.0)
        raw_position = read_list(
            reader, DicomTag.IMAGE_POSITION_PATIENT) or [0.0, 0.0, 0.0]
        position = SlicePosition(*list(map(float, raw_position)))
        height = read_int(reader, DicomTag.ROWS) or 0
        width = read_int(reader, DicomTag.COLUMNS) or 0

    except RuntimeError:
        logger.error('User sent a file that is not a DICOM.')
        SlicesRepository.delete_slice_by_id(_slice.id)
        ScansRepository.reduce_number_of_declared_slices(_slice.scan_id)
        os.unlink(temp_file.name)
        trigger_scan_conversion_if_needed(_slice.scan_id)
        return

    # Remove temporary file
    os.unlink(temp_file.name)

    _slice.update_location(location)
    _slice.update_position(position)
    _slice.update_size(height, width)
    _slice.update_status(SliceStatus.STORED)
    logger.info('"%s" updated.', _slice)

    trigger_scan_conversion_if_needed(_slice.scan_id)
Example #4
0
def get_slices_for_scan(
    scan_id: ScanID,
    begin: int,
    count: int,
    orientation: SliceOrientation = SliceOrientation.Z
) -> Iterable[Tuple[Slice, bytes]]:
    """Fetch multiple slices for given scan.

    :param scan_id: ID of a given scan
    :param begin: first slice index (included)
    :param count: number of slices that will be returned
    :param orientation: orientation for Slices (by default set to Z axis)
    :return: generator for Slices
    """
    slices = SlicesRepository.get_slices_by_scan_id(scan_id,
                                                    orientation=orientation)
    for _slice in slices[begin:begin + count]:
        image = SlicesRepository.get_slice_converted_image(_slice.id)
        yield _slice, image
Example #5
0
def convert_scan_to_png(scan_id: ScanID) -> None:
    """Convert DICOM Scan to PNG and save it into Storage.

    :param scan_id: ID of a Scan
    """
    logger.info('Starting Scan (%s) conversion.', scan_id)
    temp_files_to_remove: List[str] = []
    scan = ScansRepository.get_scan_by_id(scan_id)
    slices = SlicesRepository.get_slices_by_scan_id(scan_id)
    if scan.declared_number_of_slices == 0:
        logger.error('This Scan is empty! Removing from database...')
        ScansRepository.delete_scan_by_id(scan_id)
        return

    logger.info('Marking Scan as processing.')
    scan.update_status(ScanStatus.PROCESSING)

    # At first, collect all Dicom images for given Scan
    logger.info('Reading all Slices for this Scan... This may take a while...')
    dicom_images = []
    for _slice in slices:
        image = SlicesRepository.get_slice_original_image(_slice.id)

        # We've got to store above DICOM image bytes on disk due to the fact that SimpleITK does not support
        # reading files from memory. It has to work on a file stored on a hard drive.
        temp_file = NamedTemporaryFile(delete=False)
        temp_file.write(image)
        temp_file.close()

        dicom_image = sitk.ReadImage(temp_file.name)
        dicom_images.append(dicom_image)
        temp_files_to_remove.append(temp_file.name)

    # Correlate Dicom files with Slices and convert all Slices
    _convert_scan_in_all_axes(dicom_images, slices, scan)

    logger.info('Marking Scan as available to use.')
    scan.update_status(ScanStatus.AVAILABLE)

    # Remove all temporarily created files for applying workaround
    for file_name in temp_files_to_remove:
        os.unlink(file_name)
Example #6
0
def test_scan_upload_and_conversion(prepare_environment: Any,
                                    synchronous_celery: Any) -> None:
    """Test application for Scan upload and conversion."""
    api_client = get_api_client()
    user_token = get_token_for_logged_in_user('admin')

    # Step 1. Add Scan to the system
    payload = {'category': 'LUNGS', 'number_of_slices': 3}
    response = api_client.post('/api/v1/scans/',
                               data=json.dumps(payload),
                               headers=get_headers(token=user_token,
                                                   json=True))
    json_response = json.loads(response.data)
    scan_id = json_response['scan_id']

    # Step 2. Send Slices
    for file in glob.glob('tests/assets/example_scan/*.dcm'):
        with open(file, 'rb') as image:
            response = api_client.post(
                '/api/v1/scans/{}/slices'.format(scan_id),
                data={
                    'image': (image, 'slice_1.dcm'),
                },
                headers=get_headers(token=user_token, multipart=True))
            assert response.status_code == 201

    # Step 3. Check Scan & Slices in the databases
    z_slices = SlicesRepository.get_slices_by_scan_id(scan_id)
    assert len(z_slices) == 3
    y_slices = SlicesRepository.get_slices_by_scan_id(scan_id,
                                                      SliceOrientation.Y)
    assert not y_slices
    x_slices = SlicesRepository.get_slices_by_scan_id(scan_id,
                                                      SliceOrientation.X)
    assert not x_slices

    # Step 3.1. Slices in Z axis
    z_slice = SlicesRepository.get_slice_converted_image(z_slices[2].id)
    z_slice_image = Image.open(io.BytesIO(z_slice))
    assert z_slice_image.size == (512, 512)
Example #7
0
        scan.owner_id,
        'slices': [{
            'id': scan_slice.id,
            'location': scan_slice.location,
            'position_x': scan_slice.position_x,
            'position_y': scan_slice.position_y,
            'position_z': scan_slice.position_z,
            'stored': scan_slice.stored,
            'converted': scan_slice.converted,
        } for scan_slice in scan.stored_slices],
    })

    # Save DICOMs to the Scan directory
    for scan_slice in scan.stored_slices:
        slice_file_name = scan_directory + str(scan_slice.id) + '.dcm'
        slice_bytes = SlicesRepository.get_slice_original_image(scan_slice.id)
        print('Saving DICOM to file: {}'.format(slice_file_name))
        with open(slice_file_name, 'wb') as slice_file:
            slice_file.write(slice_bytes)
print('\nSaving all Scans to file: {}'.format(SCANS_METADATA_FILE))
with open(SCANS_METADATA_FILE, 'w') as json_file:
    json.dump(_scans, json_file)

# Save Labels to JSON file
_labels = {
    'labels': [],
}  # type: Dict[str, List[Dict]]
print('\nFetching all Labels...')
for label in LabelsRepository.get_all_labels():
    print('Saving Label: {}'.format(label.id))
    _labels['labels'].append({