From 8b0c61ef01e020a96815b48e622bb54315b5d9f2 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 2 Jun 2016 17:40:35 -0700 Subject: [PATCH 1/4] Add ImageItem tests, fix image downsampling bug --- pyqtgraph/graphicsItems/ImageItem.py | 7 +- .../graphicsItems/tests/test_ImageItem.py | 108 ++++++++++++++++-- 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/pyqtgraph/graphicsItems/ImageItem.py b/pyqtgraph/graphicsItems/ImageItem.py index f6597a9b..13cc9dce 100644 --- a/pyqtgraph/graphicsItems/ImageItem.py +++ b/pyqtgraph/graphicsItems/ImageItem.py @@ -300,8 +300,11 @@ class ImageItem(GraphicsObject): y = self.mapToDevice(QtCore.QPointF(0,1)) w = Point(x-o).length() h = Point(y-o).length() - xds = int(1/max(1, w)) - yds = int(1/max(1, h)) + if w == 0 or h == 0: + self.qimage = None + return + xds = int(1.0/w) + yds = int(1.0/h) image = fn.downsample(self.image, xds, axis=0) image = fn.downsample(image, yds, axis=1) else: diff --git a/pyqtgraph/graphicsItems/tests/test_ImageItem.py b/pyqtgraph/graphicsItems/tests/test_ImageItem.py index 98c79790..c9b7b4fd 100644 --- a/pyqtgraph/graphicsItems/tests/test_ImageItem.py +++ b/pyqtgraph/graphicsItems/tests/test_ImageItem.py @@ -1,17 +1,109 @@ -import gc -import weakref +import time import pytest -# try: -# import faulthandler -# faulthandler.enable() -# except ImportError: -# pass - from pyqtgraph.Qt import QtCore, QtGui, QtTest import numpy as np import pyqtgraph as pg +from pyqtgraph.tests import assertImageApproved + app = pg.mkQApp() + +def test_ImageItem(): + + view = pg.plot() + view.resize(200, 200) + img = pg.ImageItem(border=0.5) + view.addItem(img) + + + # test mono float + np.random.seed(0) + data = np.random.normal(size=(20, 20)) + dmax = data.max() + data[:10, 1] = dmax + 10 + data[1, :10] = dmax + 12 + data[3, :10] = dmax + 13 + img.setImage(data) + + QtTest.QTest.qWaitForWindowShown(view) + time.sleep(0.1) + app.processEvents() + assertImageApproved(view, 'imageitem/init', 'Init image item. View is auto-scaled, image axis 0 marked by 1 line, axis 1 is marked by 2 lines. Origin in bottom-left.') + + # ..with colormap + cmap = pg.ColorMap([0, 0.25, 0.75, 1], [[0, 0, 0, 255], [255, 0, 0, 255], [255, 255, 0, 255], [255, 255, 255, 255]]) + img.setLookupTable(cmap.getLookupTable()) + assertImageApproved(view, 'imageitem/lut', 'Set image LUT.') + + # ..and different levels + img.setLevels([dmax+9, dmax+13]) + assertImageApproved(view, 'imageitem/levels1', 'Levels show only axis lines.') + + img.setLookupTable(None) + + # test mono int + data = np.fromfunction(lambda x,y: x+y*10, (129, 128)).astype(np.int16) + img.setImage(data) + assertImageApproved(view, 'imageitem/gradient_mono_int', 'Mono int gradient.') + + img.setLevels([640, 641]) + assertImageApproved(view, 'imageitem/gradient_mono_int_levels', 'Mono int gradient w/ levels to isolate diagonal.') + + # test mono byte + data = np.fromfunction(lambda x,y: x+y, (129, 128)).astype(np.ubyte) + img.setImage(data) + assertImageApproved(view, 'imageitem/gradient_mono_byte', 'Mono byte gradient.') + + img.setLevels([127, 128]) + assertImageApproved(view, 'imageitem/gradient_mono_byte_levels', 'Mono byte gradient w/ levels to isolate diagonal.') + + # test RGBA byte + data = np.zeros((100, 100, 4), dtype='ubyte') + data[..., 0] = np.linspace(0, 255, 100).reshape(100, 1) + data[..., 1] = np.linspace(0, 255, 100).reshape(1, 100) + data[..., 3] = 255 + img.setImage(data) + assertImageApproved(view, 'imageitem/gradient_rgba_byte', 'RGBA byte gradient.') + + img.setLevels([[128, 129], [128, 255], [0, 1], [0, 255]]) + assertImageApproved(view, 'imageitem/gradient_rgba_byte_levels', 'RGBA byte gradient. Levels set to show x=128 and y>128.') + + # test RGBA float + data = data.astype(float) + img.setImage(data / 1e9) + assertImageApproved(view, 'imageitem/gradient_rgba_float', 'RGBA float gradient.') + + # checkerboard to test alpha + img2 = pg.ImageItem() + img2.setImage(np.fromfunction(lambda x,y: (x+y)%2, (10, 10)), levels=[-1,2]) + view.addItem(img2) + img2.scale(10, 10) + img2.setZValue(-10) + + data[..., 0] *= 1e-9 + data[..., 1] *= 1e9 + data[..., 3] = np.fromfunction(lambda x,y: np.sin(0.1 * (x+y)), (100, 100)) + img.setImage(data, levels=[[0, 128e-9],[0, 128e9],[0, 1],[-1, 1]]) + assertImageApproved(view, 'imageitem/gradient_rgba_float_alpha', 'RGBA float gradient with alpha.') + + # test composition mode + img.setCompositionMode(QtGui.QPainter.CompositionMode_Plus) + assertImageApproved(view, 'imageitem/gradient_rgba_float_additive', 'RGBA float gradient with alpha and additive composition mode.') + + img2.hide() + img.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) + + # test downsampling + data = np.fromfunction(lambda x,y: np.cos(0.002 * x**2), (800, 100)) + img.setImage(data, levels=[-1, 1]) + assertImageApproved(view, 'imageitem/resolution_without_downsampling', 'Resolution test without downsampling.') + + img.setAutoDownsample(True) + assertImageApproved(view, 'imageitem/resolution_with_downsampling_x', 'Resolution test with downsampling axross x axis.') + + img.setImage(data.T, levels=[-1, 1]) + assertImageApproved(view, 'imageitem/resolution_with_downsampling_y', 'Resolution test with downsampling across y axis.') + @pytest.mark.skipif(pg.Qt.USE_PYSIDE, reason="pyside does not have qWait") def test_dividebyzero(): import pyqtgraph as pg From e36fca8f49468b8ca37ebc84a772586935d0d4ff Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 2 Jun 2016 17:44:48 -0700 Subject: [PATCH 2/4] Update test data tag --- pyqtgraph/tests/image_testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyqtgraph/tests/image_testing.py b/pyqtgraph/tests/image_testing.py index a2b20ee7..bab3acc4 100644 --- a/pyqtgraph/tests/image_testing.py +++ b/pyqtgraph/tests/image_testing.py @@ -42,7 +42,7 @@ Procedure for unit-testing with images: # pyqtgraph should be tested against. When adding or changing test images, # create and push a new tag and update this variable. To test locally, begin # by creating the tag in your ~/.pyqtgraph/test-data repository. -testDataTag = 'test-data-4' +testDataTag = 'test-data-5' import time From 0172d7b1e40de42fe1bdae876206138038132da6 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 3 Jun 2016 17:30:05 -0700 Subject: [PATCH 3/4] Fix pixel error in image tests by preventing an extra plot window from opening (no idea why this happens) --- .../graphicsItems/tests/test_ScatterPlotItem.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pyqtgraph/graphicsItems/tests/test_ScatterPlotItem.py b/pyqtgraph/graphicsItems/tests/test_ScatterPlotItem.py index 8b0ebc8f..acf6ad72 100644 --- a/pyqtgraph/graphicsItems/tests/test_ScatterPlotItem.py +++ b/pyqtgraph/graphicsItems/tests/test_ScatterPlotItem.py @@ -1,15 +1,15 @@ import pyqtgraph as pg import numpy as np app = pg.mkQApp() -plot = pg.plot() app.processEvents() -# set view range equal to its bounding rect. -# This causes plots to look the same regardless of pxMode. -plot.setRange(rect=plot.boundingRect()) def test_scatterplotitem(): + plot = pg.PlotWidget() + # set view range equal to its bounding rect. + # This causes plots to look the same regardless of pxMode. + plot.setRange(rect=plot.boundingRect()) for i, pxMode in enumerate([True, False]): for j, useCache in enumerate([True, False]): s = pg.ScatterPlotItem() @@ -54,6 +54,10 @@ def test_scatterplotitem(): def test_init_spots(): + plot = pg.PlotWidget() + # set view range equal to its bounding rect. + # This causes plots to look the same regardless of pxMode. + plot.setRange(rect=plot.boundingRect()) spots = [ {'x': 0, 'y': 1}, {'pos': (1, 2), 'pen': None, 'brush': None, 'data': 'zzz'}, From e46be6ddecb328a5c75563b1a5910ef7b7a6c5c6 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Mon, 18 Jul 2016 17:35:33 -0700 Subject: [PATCH 4/4] Remove axes from tests; these break CI tests. --- .../graphicsItems/tests/test_ImageItem.py | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/pyqtgraph/graphicsItems/tests/test_ImageItem.py b/pyqtgraph/graphicsItems/tests/test_ImageItem.py index c9b7b4fd..d13d703c 100644 --- a/pyqtgraph/graphicsItems/tests/test_ImageItem.py +++ b/pyqtgraph/graphicsItems/tests/test_ImageItem.py @@ -10,8 +10,11 @@ app = pg.mkQApp() def test_ImageItem(): - view = pg.plot() - view.resize(200, 200) + w = pg.GraphicsWindow() + view = pg.ViewBox() + w.setCentralWidget(view) + w.resize(200, 200) + w.show() img = pg.ImageItem(border=0.5) view.addItem(img) @@ -25,37 +28,37 @@ def test_ImageItem(): data[3, :10] = dmax + 13 img.setImage(data) - QtTest.QTest.qWaitForWindowShown(view) + QtTest.QTest.qWaitForWindowShown(w) time.sleep(0.1) app.processEvents() - assertImageApproved(view, 'imageitem/init', 'Init image item. View is auto-scaled, image axis 0 marked by 1 line, axis 1 is marked by 2 lines. Origin in bottom-left.') + assertImageApproved(w, 'imageitem/init', 'Init image item. View is auto-scaled, image axis 0 marked by 1 line, axis 1 is marked by 2 lines. Origin in bottom-left.') # ..with colormap cmap = pg.ColorMap([0, 0.25, 0.75, 1], [[0, 0, 0, 255], [255, 0, 0, 255], [255, 255, 0, 255], [255, 255, 255, 255]]) img.setLookupTable(cmap.getLookupTable()) - assertImageApproved(view, 'imageitem/lut', 'Set image LUT.') + assertImageApproved(w, 'imageitem/lut', 'Set image LUT.') # ..and different levels img.setLevels([dmax+9, dmax+13]) - assertImageApproved(view, 'imageitem/levels1', 'Levels show only axis lines.') + assertImageApproved(w, 'imageitem/levels1', 'Levels show only axis lines.') img.setLookupTable(None) # test mono int data = np.fromfunction(lambda x,y: x+y*10, (129, 128)).astype(np.int16) img.setImage(data) - assertImageApproved(view, 'imageitem/gradient_mono_int', 'Mono int gradient.') + assertImageApproved(w, 'imageitem/gradient_mono_int', 'Mono int gradient.') img.setLevels([640, 641]) - assertImageApproved(view, 'imageitem/gradient_mono_int_levels', 'Mono int gradient w/ levels to isolate diagonal.') + assertImageApproved(w, 'imageitem/gradient_mono_int_levels', 'Mono int gradient w/ levels to isolate diagonal.') # test mono byte data = np.fromfunction(lambda x,y: x+y, (129, 128)).astype(np.ubyte) img.setImage(data) - assertImageApproved(view, 'imageitem/gradient_mono_byte', 'Mono byte gradient.') + assertImageApproved(w, 'imageitem/gradient_mono_byte', 'Mono byte gradient.') img.setLevels([127, 128]) - assertImageApproved(view, 'imageitem/gradient_mono_byte_levels', 'Mono byte gradient w/ levels to isolate diagonal.') + assertImageApproved(w, 'imageitem/gradient_mono_byte_levels', 'Mono byte gradient w/ levels to isolate diagonal.') # test RGBA byte data = np.zeros((100, 100, 4), dtype='ubyte') @@ -63,15 +66,15 @@ def test_ImageItem(): data[..., 1] = np.linspace(0, 255, 100).reshape(1, 100) data[..., 3] = 255 img.setImage(data) - assertImageApproved(view, 'imageitem/gradient_rgba_byte', 'RGBA byte gradient.') + assertImageApproved(w, 'imageitem/gradient_rgba_byte', 'RGBA byte gradient.') img.setLevels([[128, 129], [128, 255], [0, 1], [0, 255]]) - assertImageApproved(view, 'imageitem/gradient_rgba_byte_levels', 'RGBA byte gradient. Levels set to show x=128 and y>128.') + assertImageApproved(w, 'imageitem/gradient_rgba_byte_levels', 'RGBA byte gradient. Levels set to show x=128 and y>128.') # test RGBA float data = data.astype(float) img.setImage(data / 1e9) - assertImageApproved(view, 'imageitem/gradient_rgba_float', 'RGBA float gradient.') + assertImageApproved(w, 'imageitem/gradient_rgba_float', 'RGBA float gradient.') # checkerboard to test alpha img2 = pg.ImageItem() @@ -84,11 +87,11 @@ def test_ImageItem(): data[..., 1] *= 1e9 data[..., 3] = np.fromfunction(lambda x,y: np.sin(0.1 * (x+y)), (100, 100)) img.setImage(data, levels=[[0, 128e-9],[0, 128e9],[0, 1],[-1, 1]]) - assertImageApproved(view, 'imageitem/gradient_rgba_float_alpha', 'RGBA float gradient with alpha.') + assertImageApproved(w, 'imageitem/gradient_rgba_float_alpha', 'RGBA float gradient with alpha.') # test composition mode img.setCompositionMode(QtGui.QPainter.CompositionMode_Plus) - assertImageApproved(view, 'imageitem/gradient_rgba_float_additive', 'RGBA float gradient with alpha and additive composition mode.') + assertImageApproved(w, 'imageitem/gradient_rgba_float_additive', 'RGBA float gradient with alpha and additive composition mode.') img2.hide() img.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) @@ -96,13 +99,14 @@ def test_ImageItem(): # test downsampling data = np.fromfunction(lambda x,y: np.cos(0.002 * x**2), (800, 100)) img.setImage(data, levels=[-1, 1]) - assertImageApproved(view, 'imageitem/resolution_without_downsampling', 'Resolution test without downsampling.') + assertImageApproved(w, 'imageitem/resolution_without_downsampling', 'Resolution test without downsampling.') img.setAutoDownsample(True) - assertImageApproved(view, 'imageitem/resolution_with_downsampling_x', 'Resolution test with downsampling axross x axis.') + assertImageApproved(w, 'imageitem/resolution_with_downsampling_x', 'Resolution test with downsampling axross x axis.') img.setImage(data.T, levels=[-1, 1]) - assertImageApproved(view, 'imageitem/resolution_with_downsampling_y', 'Resolution test with downsampling across y axis.') + assertImageApproved(w, 'imageitem/resolution_with_downsampling_y', 'Resolution test with downsampling across y axis.') + @pytest.mark.skipif(pg.Qt.USE_PYSIDE, reason="pyside does not have qWait") def test_dividebyzero():