comparison Plugins/DicomWeb/Run.py @ 434:2c142e070f19 Orthanc-1.9.7

dicomweb: added test_multiframe_windowing
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 30 Aug 2021 18:13:08 +0200
parents dd519677974d
children e769bcf2b94f
comparison
equal deleted inserted replaced
433:cb579ad96a6c 434:2c142e070f19
1396 self.assertEqual('JPEG', im1.format) 1396 self.assertEqual('JPEG', im1.format)
1397 1397
1398 im2 = GetImage(ORTHANC, args.wado + '?requestType=WADO&objectUID=%s&contentType=image/png' % INSTANCE) 1398 im2 = GetImage(ORTHANC, args.wado + '?requestType=WADO&objectUID=%s&contentType=image/png' % INSTANCE)
1399 self.assertEqual('PNG', im2.format) 1399 self.assertEqual('PNG', im2.format)
1400 1400
1401 im3 = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered' % (STUDY, SERIES, INSTANCE)) 1401 im3 = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered?window=200,800,linear' % (STUDY, SERIES, INSTANCE))
1402 self.assertEqual('JPEG', im3.format) 1402 self.assertEqual('JPEG', im3.format)
1403 1403
1404 im4 = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/rendered' % (STUDY, SERIES, INSTANCE), 1404 im4 = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/rendered?window=200,800,linear' % (STUDY, SERIES, INSTANCE),
1405 headers = { 'Accept' : 'image/png' }) 1405 headers = { 'Accept' : 'image/png' })
1406 self.assertEqual('PNG', im4.format) 1406 self.assertEqual('PNG', im4.format)
1407 1407
1408 im5 = GetImage(ORTHANC, '/instances/%s/rendered' % i, { 'Accept' : 'image/jpeg' }) 1408 im5 = GetImage(ORTHANC, '/instances/%s/rendered' % i, { 'Accept' : 'image/jpeg' })
1409 self.assertEqual('JPEG', im5.format) 1409 self.assertEqual('JPEG', im5.format)
1414 for im in [ truth, im1, im2, im3, im4, im5, im6 ]: 1414 for im in [ truth, im1, im2, im3, im4, im5, im6 ]:
1415 self.assertEqual('L', im.mode) 1415 self.assertEqual('L', im.mode)
1416 self.assertEqual(512, im.size[0]) 1416 self.assertEqual(512, im.size[0])
1417 self.assertEqual(512, im.size[1]) 1417 self.assertEqual(512, im.size[1])
1418 1418
1419 im2.save('/tmp/a.png')
1420 im4.save('/tmp/b.png')
1421 im6.save('/tmp/c.png')
1422
1423 # The following fails in DICOMweb plugin <= 1.2, as "/rendered" 1419 # The following fails in DICOMweb plugin <= 1.2, as "/rendered"
1424 # was redirecting to the "/preview" route of Orthanc 1420 # was redirecting to the "/preview" route of Orthanc
1425 # http://effbot.org/zone/pil-comparing-images.htm 1421 # http://effbot.org/zone/pil-comparing-images.htm
1426 self.assertTrue(ImageChops.difference(im1, im3).getbbox() is None) 1422 self.assertLess(ImageChops.difference(im1, im3).getextrema() [1], 10)
1423 self.assertLess(ImageChops.difference(im2, im4).getextrema() [1], 2)
1424 self.assertLess(ImageChops.difference(im3, im5).getextrema() [1], 10)
1425 self.assertLess(ImageChops.difference(im4, im6).getextrema() [1], 2)
1427 self.assertTrue(ImageChops.difference(im1, im5).getbbox() is None) 1426 self.assertTrue(ImageChops.difference(im1, im5).getbbox() is None)
1428 self.assertTrue(ImageChops.difference(im2, im4).getbbox() is None)
1429 self.assertTrue(ImageChops.difference(im2, im6).getbbox() is None) 1427 self.assertTrue(ImageChops.difference(im2, im6).getbbox() is None)
1430 self.assertTrue(ImageChops.difference(im3, im5).getbbox() is None)
1431 self.assertTrue(ImageChops.difference(im4, im6).getbbox() is None)
1432 1428
1433 bbox = ImageChops.difference(im2, truth).getbbox() 1429 bbox = ImageChops.difference(im2, truth).getbbox()
1434 if bbox != None: 1430 if bbox != None:
1435 # Tolerance of just 1 pixel of difference (needed on Windows) 1431 # Tolerance of just 1 pixel of difference (needed on Windows)
1436 #print(im2.getpixel((238,275))) # => 255 1432 #print(im2.getpixel((238,275))) # => 255
1473 self.assertEqual("Hello1", b[0]["0008103E"]["Value"][0]) 1469 self.assertEqual("Hello1", b[0]["0008103E"]["Value"][0])
1474 1470
1475 1471
1476 DoDelete(ORTHANC, 'instances/%s' % a) 1472 DoDelete(ORTHANC, 'instances/%s' % a)
1477 a = UploadInstance(ORTHANC, 'Issue195-bis.dcm') ['ID'] 1473 a = UploadInstance(ORTHANC, 'Issue195-bis.dcm') ['ID']
1478 URI = 'dicom-web/studies/1.2.276.0.7230010.3.1.2.8323329.23653.1620311964.865418/series/1.2.276.0.7230010.3.1.3.8323329.23653.1620311964.865419/instances/1.2.276.0.7230010.3.1.4.8323329.23653.1620311964.865420' 1474 URI = 'dicom-web/studies/1.2.276.0.7230010.3.1.2.8323329.6792.1625504071.652468/series/1.2.276.0.7230010.3.1.3.8323329.6792.1625504071.652469/instances/1.2.276.0.7230010.3.1.4.8323329.6792.1625504071.652470'
1479 b = DoGet(ORTHANC, '%s/metadata' % URI, 1475 b = DoGet(ORTHANC, '%s/metadata' % URI,
1480 headers = { 'Accept' : 'application/dicom+json' }) 1476 headers = { 'Accept' : 'application/dicom+json' })
1481 1477
1482 self.assertEqual(1, len(b)) 1478 self.assertEqual(1, len(b))
1483 self.assertEqual(5, len(b[0])) 1479 self.assertEqual(5, len(b[0]))
1484 1480
1485 # The expected result can be found by typing "dcm2json Database/Issue195-bis.dcm" 1481 # The expected result can be found by typing "dcm2json ../../Database/Issue195-bis.dcm"
1486 self.assertEqual(2, len(b[0]["00080018"])) 1482 self.assertEqual(2, len(b[0]["00080018"]))
1487 self.assertEqual("UI", b[0]["00080018"]["vr"]) 1483 self.assertEqual("UI", b[0]["00080018"]["vr"])
1488 self.assertEqual("1.2.276.0.7230010.3.1.4.8323329.23653.1620311964.865420", 1484 self.assertEqual("1.2.276.0.7230010.3.1.4.8323329.6792.1625504071.652470",
1489 b[0]["00080018"]["Value"][0]) 1485 b[0]["00080018"]["Value"][0])
1490 1486
1491 self.assertEqual(2, len(b[0]["0020000D"])) 1487 self.assertEqual(2, len(b[0]["0020000D"]))
1492 self.assertEqual("UI", b[0]["0020000D"]["vr"]) 1488 self.assertEqual("UI", b[0]["0020000D"]["vr"])
1493 self.assertEqual("1.2.276.0.7230010.3.1.2.8323329.23653.1620311964.865418", 1489 self.assertEqual("1.2.276.0.7230010.3.1.2.8323329.6792.1625504071.652468",
1494 b[0]["0020000D"]["Value"][0]) 1490 b[0]["0020000D"]["Value"][0])
1495 1491
1496 self.assertEqual(2, len(b[0]["0020000E"])) 1492 self.assertEqual(2, len(b[0]["0020000E"]))
1497 self.assertEqual("UI", b[0]["0020000E"]["vr"]) 1493 self.assertEqual("UI", b[0]["0020000E"]["vr"])
1498 self.assertEqual("1.2.276.0.7230010.3.1.3.8323329.23653.1620311964.865419", 1494 self.assertEqual("1.2.276.0.7230010.3.1.3.8323329.6792.1625504071.652469",
1499 b[0]["0020000E"]["Value"][0]) 1495 b[0]["0020000E"]["Value"][0])
1500 1496
1501 self.assertEqual(2, len(b[0]["0008103E"])) 1497 self.assertEqual(2, len(b[0]["00084567"]))
1502 self.assertEqual("UN", b[0]["0008103E"]["vr"]) 1498 self.assertEqual("UN", b[0]["00084567"]["vr"])
1503 self.assertEqual('http://%s:%s%s' % (args.server, args.rest, '/%s/bulk/0008103e' % URI), 1499 self.assertEqual('http://%s:%s%s' % (args.server, args.rest, '/%s/bulk/00084567' % URI),
1504 b[0]["0008103E"]["BulkDataURI"]) 1500 b[0]["00084567"]["BulkDataURI"])
1505 1501
1506 c = DoGet(ORTHANC, '%s/bulk/0008103e' % URI) 1502 c = DoGet(ORTHANC, '%s/bulk/00084567' % URI)
1507 self.assertTrue('Content-Length: 2\r\n' in c) 1503 self.assertTrue('Content-Length: 2\r\n' in c)
1508 index = c.find('\r\n\r\n') 1504 index = c.find('\r\n\r\n')
1509 self.assertEqual(0x42, ord(c[index + 4])) 1505 self.assertEqual(0x42, ord(c[index + 4]))
1510 self.assertEqual(0x00, ord(c[index + 5])) 1506 self.assertEqual(0x00, ord(c[index + 5]))
1511 1507
1512 # Case of an empty value, fails in Orthanc <= 1.9.2 because of issue #195 1508 # Case of an empty value, fails in Orthanc <= 1.9.2 because of issue #195
1513 self.assertEqual(1, len(b[0]["00081030"])) 1509 self.assertEqual(1, len(b[0]["00084565"]))
1514 self.assertEqual("UN", b[0]["00081030"]["vr"]) 1510 self.assertEqual("UN", b[0]["00084565"]["vr"])
1515 1511
1516 1512
1513
1514
1515 def test_multiframe_windowing(self):
1516 # Fixed in DICOMweb 1.7
1517 def GetLinear(x, c, w):
1518 # http://dicom.nema.org/MEDICAL/dicom/2019a/output/chtml/part03/sect_C.11.2.html#sect_C.11.2.1.2.1
1519 ymin = 0.0
1520 ymax = 255.0
1521 if float(x) <= float(c) - 0.5 - (float(w) - 1.0) / 2.0:
1522 return ymin
1523 elif float(x) > float(c) - 0.5 + (float(w) - 1.0) / 2.0 :
1524 return ymax
1525 else:
1526 return ((float(x) - (float(c) - 0.5)) / (float(w) - 1.0) + 0.5) * (ymax - ymin) + ymin
1527
1528 def GetLinearExact(x, c, w):
1529 # http://dicom.nema.org/MEDICAL/dicom/2019a/output/chtml/part03/sect_C.11.2.html#sect_C.11.2.1.3.2
1530 ymin = 0.0
1531 ymax = 255.0
1532 if float(x) <= float(c) - float(w) / 2.0:
1533 return ymin
1534 elif float(x) > float(c) + float(w) / 2.0:
1535 return ymax
1536 else:
1537 return (float(x) - float(c)) / float(w) * (ymax- ymin) + ymin
1538
1539 def GetSigmoid(x, c, w):
1540 # http://dicom.nema.org/MEDICAL/dicom/2019a/output/chtml/part03/sect_C.11.2.html#sect_C.11.2.1.3.1
1541 ymin = 0.0
1542 ymax = 255.0
1543 return (ymax - ymin) / (1.0 + math.exp(-4 * (float(x) - float(c)) / float(w)))
1544
1545 self.assertAlmostEqual(GetLinear(10, 0, 100), 154.54545454545456)
1546 self.assertAlmostEqual(GetLinear(-1000, 2048, 4096), 0)
1547 self.assertAlmostEqual(GetLinear(5096, 2048, 4096), 255)
1548 self.assertAlmostEqual(GetLinear(333, 2048, 4096), 20.7362637362637)
1549 self.assertAlmostEqual(GetLinear(16, 127, 256), 17)
1550
1551 self.assertAlmostEqual(GetLinearExact(-1000, 2048, 4096), 0)
1552 self.assertAlmostEqual(GetLinearExact(5096, 2048, 4096), 255)
1553 self.assertAlmostEqual(GetLinearExact(150, 127, 256), 22.91015625)
1554
1555 self.assertAlmostEqual(GetSigmoid(150, 127, 256), 150.166728345797)
1556
1557 UploadInstance(ORTHANC, 'MultiframeWindowing.dcm')
1558 STUDY = '1.2.840.113619.2.176.2025.1499492.7391.1175285944.390'
1559 SERIES = '1.2.840.113619.2.176.2025.1499492.7391.1175285944.394'
1560 INSTANCE = '1.2.840.113619.2.176.2025.1499492.7040.1175286242.109'
1561
1562 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered?window=127,256,linear' % (STUDY, SERIES, INSTANCE))
1563 self.assertLessEqual(abs(GetLinear(0x00, 127, 256) - im.getpixel((0, 0))), 1)
1564 self.assertLessEqual(abs(GetLinear(0x10, 127, 256) - im.getpixel((1, 0))), 1)
1565 self.assertLessEqual(abs(GetLinear(0x20, 127, 256) - im.getpixel((0, 1))), 1.1)
1566 self.assertLessEqual(abs(GetLinear(0x30, 127, 256) - im.getpixel((1, 1))), 1.1)
1567
1568 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered?window=0,256,linear-exact' % (STUDY, SERIES, INSTANCE))
1569 self.assertLessEqual(abs(GetLinearExact(0x00, 0, 256) - im.getpixel((0, 0))), 1)
1570 self.assertLessEqual(abs(GetLinearExact(0x10, 0, 256) - im.getpixel((1, 0))), 1)
1571 self.assertLessEqual(abs(GetLinearExact(0x20, 0, 256) - im.getpixel((0, 1))), 1.2)
1572 self.assertLessEqual(abs(GetLinearExact(0x30, 0, 256) - im.getpixel((1, 1))), 1.2)
1573
1574 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered?window=127,256,sigmoid' % (STUDY, SERIES, INSTANCE))
1575 self.assertLessEqual(abs(GetSigmoid(0x00, 127, 256) - im.getpixel((0, 0))), 3)
1576 self.assertLessEqual(abs(GetSigmoid(0x10, 127, 256) - im.getpixel((1, 0))), 1)
1577 self.assertLessEqual(abs(GetSigmoid(0x20, 127, 256) - im.getpixel((0, 1))), 1)
1578 self.assertLessEqual(abs(GetSigmoid(0x30, 127, 256) - im.getpixel((1, 1))), 1)
1579
1580 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/1/rendered?window=16,128,linear' % (STUDY, SERIES, INSTANCE))
1581 self.assertLessEqual(abs(GetLinear(0x00, 16, 128) - im.getpixel((0, 0))), 1)
1582 self.assertLessEqual(abs(GetLinear(0x10, 16, 128) - im.getpixel((1, 0))), 1)
1583 self.assertLessEqual(abs(GetLinear(0x20, 16, 128) - im.getpixel((0, 1))), 2)
1584 self.assertLessEqual(abs(GetLinear(0x30, 16, 128) - im.getpixel((1, 1))), 2)
1585
1586 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/2/rendered?window=127,256,linear' % (STUDY, SERIES, INSTANCE))
1587 ri = 100.0
1588 rs = 1.0
1589 self.assertLessEqual(abs(GetLinear(0x00 * rs + ri, 127, 256) - im.getpixel((0, 0))), 1)
1590 self.assertLessEqual(abs(GetLinear(0x10 * rs + ri, 127, 256) - im.getpixel((1, 0))), 1)
1591 self.assertLessEqual(abs(GetLinear(0x20 * rs + ri, 127, 256) - im.getpixel((0, 1))), 1)
1592 self.assertLessEqual(abs(GetLinear(0x30 * rs + ri, 127, 256) - im.getpixel((1, 1))), 1)
1593
1594 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/3/rendered?window=127,256,linear' % (STUDY, SERIES, INSTANCE))
1595 ri = 0.0
1596 rs = 2.0
1597 self.assertLessEqual(abs(GetLinear(0x00 * rs + ri, 127, 256) - im.getpixel((0, 0))), 1)
1598 self.assertLessEqual(abs(GetLinear(0x10 * rs + ri, 127, 256) - im.getpixel((1, 0))), 1.1)
1599 self.assertLessEqual(abs(GetLinear(0x20 * rs + ri, 127, 256) - im.getpixel((0, 1))), 1)
1600 self.assertLessEqual(abs(GetLinear(0x30 * rs + ri, 127, 256) - im.getpixel((1, 1))), 1)
1601
1602 im = GetImage(ORTHANC, '/dicom-web/studies/%s/series/%s/instances/%s/frames/4/rendered?window=127,256,linear' % (STUDY, SERIES, INSTANCE))
1603 ri = 100.0
1604 rs = 2.0
1605 self.assertLessEqual(abs(GetLinear(0x00 * rs + ri, 127, 256) - im.getpixel((0, 0))), 1)
1606 self.assertLessEqual(abs(GetLinear(0x10 * rs + ri, 127, 256) - im.getpixel((1, 0))), 1)
1607 self.assertLessEqual(abs(GetLinear(0x20 * rs + ri, 127, 256) - im.getpixel((0, 1))), 1)
1608 self.assertLessEqual(abs(GetLinear(0x30 * rs + ri, 127, 256) - im.getpixel((1, 1))), 1)
1609
1610
1517 try: 1611 try:
1518 print('\nStarting the tests...') 1612 print('\nStarting the tests...')
1519 unittest.main(argv = [ sys.argv[0] ] + args.options) 1613 unittest.main(argv = [ sys.argv[0] ] + args.options)
1520 1614
1521 finally: 1615 finally: