|
2 | 2 | """
|
3 | 3 | from __future__ import division, print_function, absolute_import
|
4 | 4 |
|
| 5 | +import re, string |
| 6 | + |
5 | 7 | from ..py3k import asstr
|
6 | 8 |
|
7 | 9 |
|
@@ -51,3 +53,138 @@ def find_private_section(dcm_data, group_no, creator):
|
51 | 53 | if creator == name:
|
52 | 54 | return elno * 0x100
|
53 | 55 | return None
|
| 56 | + |
| 57 | + |
| 58 | +def tm_to_seconds(time_str): |
| 59 | + '''Convert a DICOM time value (value representation of 'TM') to the number |
| 60 | + of seconds past midnight. |
| 61 | +
|
| 62 | + Parameters |
| 63 | + ---------- |
| 64 | + time_str : str |
| 65 | + The string value from the DICOM element |
| 66 | +
|
| 67 | + Returns |
| 68 | + ------- |
| 69 | + sec_past_midnight : float |
| 70 | + The number of seconds past midnight |
| 71 | + ''' |
| 72 | + # Allow trailing white space |
| 73 | + time_str = time_str.rstrip() |
| 74 | + |
| 75 | + # Allow ACR/NEMA style format which includes colons between hours/minutes |
| 76 | + # and minutes/seconds |
| 77 | + colons = [x.start() for x in re.finditer(':', time_str)] |
| 78 | + if len(colons) > 0: |
| 79 | + if colons not in ([2], [2, 5]): |
| 80 | + raise ValueError("Invalid use of colons in 'TM' VR") |
| 81 | + time_str = time_str.replace(':', '') |
| 82 | + |
| 83 | + # Make sure the string length is valid |
| 84 | + str_len = len(time_str) |
| 85 | + is_valid = str_len > 0 |
| 86 | + if str_len <= 6: |
| 87 | + # If there are six or less chars, there should be an even number |
| 88 | + if str_len % 2 != 0: |
| 89 | + is_valid = False |
| 90 | + else: |
| 91 | + # If there are more than six chars, the seventh position should be |
| 92 | + # a decimal followed by at least one digit |
| 93 | + if str_len == 7 or time_str[6] != '.': |
| 94 | + is_valid = False |
| 95 | + if not is_valid: |
| 96 | + raise ValueError("Invalid number of digits for 'TM' VR") |
| 97 | + |
| 98 | + # Make sure we don't have leading white space |
| 99 | + if time_str[0] in string.whitespace: |
| 100 | + raise ValueError("Leading whitespace not allowed in 'TM' VR") |
| 101 | + |
| 102 | + # The minutes and seconds are optional |
| 103 | + result = int(time_str[:2]) * 3600 |
| 104 | + if str_len > 2: |
| 105 | + result += int(time_str[2:4]) * 60 |
| 106 | + if str_len > 4: |
| 107 | + result += float(time_str[4:]) |
| 108 | + |
| 109 | + return float(result) |
| 110 | + |
| 111 | + |
| 112 | +def seconds_to_tm(seconds): |
| 113 | + '''Convert a float representing seconds past midnight into DICOM TM value |
| 114 | +
|
| 115 | + Parameters |
| 116 | + ---------- |
| 117 | + seconds : float |
| 118 | + Number of seconds past midnights |
| 119 | +
|
| 120 | + Returns |
| 121 | + ------- |
| 122 | + tm : str |
| 123 | + String suitable for use as value in DICOM element with VR of 'TM' |
| 124 | + ''' |
| 125 | + hours = seconds // 3600 |
| 126 | + seconds -= hours * 3600 |
| 127 | + minutes = seconds // 60 |
| 128 | + seconds -= minutes * 60 |
| 129 | + res = '%02d%02d%08.5f' % (hours, minutes, seconds) |
| 130 | + return res |
| 131 | + |
| 132 | + |
| 133 | +def as_to_years(age_str): |
| 134 | + '''Convert a DICOM age value (value representation of 'AS') to the age in |
| 135 | + years. |
| 136 | +
|
| 137 | + Parameters |
| 138 | + ---------- |
| 139 | + age_str : str |
| 140 | + The string value from the DICOM element |
| 141 | +
|
| 142 | + Returns |
| 143 | + ------- |
| 144 | + age : float |
| 145 | + The age of the subject in years |
| 146 | + ''' |
| 147 | + age_str = age_str.strip() |
| 148 | + if age_str[-1] == 'Y': |
| 149 | + return float(age_str[:-1]) |
| 150 | + elif age_str[-1] == 'M': |
| 151 | + return float(age_str[:-1]) / 12 |
| 152 | + elif age_str[-1] == 'W': |
| 153 | + return float(age_str[:-1]) / (365. / 7) |
| 154 | + elif age_str[-1] == 'D': |
| 155 | + return float(age_str[:-1]) / 365 |
| 156 | + else: |
| 157 | + return float(age_str) |
| 158 | + |
| 159 | + |
| 160 | +def years_to_as(years): |
| 161 | + '''Convert float representing age in years to DICOM 'AS' value |
| 162 | +
|
| 163 | + Parameters |
| 164 | + ---------- |
| 165 | + years : float |
| 166 | + The years of age |
| 167 | +
|
| 168 | + Returns |
| 169 | + ------- |
| 170 | + as : str |
| 171 | + String suitable for use as value in DICOM element with VR of 'AS' |
| 172 | + ''' |
| 173 | + if years == round(years): |
| 174 | + return '%dY' % years |
| 175 | + |
| 176 | + # Choose how to represent the age (years, months, weeks, or days) |
| 177 | + conversions = (('Y', 1), ('M', 12), ('W', (365. / 7)), ('D', 365)) |
| 178 | + # Try all the conversions, ignore ones that have more than three digits |
| 179 | + # which is the limit for the AS value representation, or where they round |
| 180 | + # to zero |
| 181 | + results = [(years * x[1], x[0]) for x in conversions] |
| 182 | + results = [x for x in results |
| 183 | + if round(x[0]) > 0 and len('%d' % x[0]) < 4] |
| 184 | + # Choose the first one that is close to the minimum error |
| 185 | + errors = [abs(x[0] - round(x[0])) for x in results] |
| 186 | + min_error = min(errors) |
| 187 | + best_idx = 0 |
| 188 | + while errors[best_idx] - min_error > 0.001: |
| 189 | + best_idx += 1 |
| 190 | + return '%d%s' % (round(results[best_idx][0]), results[best_idx][1]) |
0 commit comments