Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-morecvutils for 
openSUSE:Factory checked in at 2026-03-30 18:31:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-morecvutils (Old)
 and      /work/SRC/openSUSE:Factory/.python-morecvutils.new.1999 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-morecvutils"

Mon Mar 30 18:31:20 2026 rev:6 rq:1343496 version:1.1.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-morecvutils/python-morecvutils.changes    
2025-05-09 18:52:47.500656623 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-morecvutils.new.1999/python-morecvutils.changes
  2026-03-30 18:34:29.863081267 +0200
@@ -1,0 +2,7 @@
+Sun Mar 29 18:02:08 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 1.1.0:
+  * modernized package
+  * typing
+
+-------------------------------------------------------------------

Old:
----
  morecvutils-1.0.2.tar.gz

New:
----
  morecvutils-1.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-morecvutils.spec ++++++
--- /var/tmp/diff_new_pack.jrF5WM/_old  2026-03-30 18:34:30.667114881 +0200
+++ /var/tmp/diff_new_pack.jrF5WM/_new  2026-03-30 18:34:30.671115048 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-morecvutils
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,15 +18,14 @@
 
 %define         pythons python3
 Name:           python-morecvutils
-Version:        1.0.2
+Version:        1.1.0
 Release:        0
 Summary:        Computer Vision utilities
 License:        MIT
 URL:            https://github.com/scivision/morecvutils
 Source:         
https://github.com/scivision/morecvutils/archive/v%{version}.tar.gz#/morecvutils-%{version}.tar.gz
 BuildRequires:  %{python_module pip >= 10}
-BuildRequires:  %{python_module pip}
-BuildRequires:  %{python_module setuptools}
+BuildRequires:  %{python_module setuptools >= 61.0.0}
 BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
@@ -56,7 +55,7 @@
 %prep
 %setup -q -n morecvutils-%{version}
 # no main section: no interpreter
-sed -i '1{/env python/d}' morecvutils/calcOptFlow.py
+sed -i '1{/env python/d}' src/morecvutils/calcOptFlow.py 
src/morecvutils/tests/test_blob.py
 # remove executable bits from Demo files
 chmod -x Demo* OpticalFlow_Python_vs_Matlab.py
 
@@ -72,10 +71,6 @@
 %{?python_compileall}
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
-# Don't put demos in bindir
-rm %{buildroot}%{_bindir}/Demo*
-rm %{buildroot}%{_bindir}/OpticalFlow_Python_vs_Matlab.py
-
 %check
 # test uses AVC/AAC file
 %pytest -k "not test_avi"

++++++ morecvutils-1.0.2.tar.gz -> morecvutils-1.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.coveragerc 
new/morecvutils-1.1.0/.coveragerc
--- old/morecvutils-1.0.2/.coveragerc   2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/.coveragerc   1970-01-01 01:00:00.000000000 +0100
@@ -1,20 +0,0 @@
-[run]
-cover_pylib = false
-omit =
-  /home/travis/virtualenv/*
-  */site-packages/*
-  */bin/*
-
-[report]
-exclude_lines =
-  pragma: no cover
-  def __repr__
-  except RuntimeError
-  except NotImplementedError
-  except ImportError
-  except FileNotFoundError
-  except CalledProcessError
-  logging.warning
-  logging.error
-  logging.critical
-  if __name__ == .__main__.:
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.flake8 
new/morecvutils-1.1.0/.flake8
--- old/morecvutils-1.0.2/.flake8       2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/.flake8       1970-01-01 01:00:00.000000000 +0100
@@ -1,3 +0,0 @@
-[flake8]
-max-line-length = 132
-exclude = .git,__pycache__,.eggs/,doc/,docs/,build/,dist/,archive/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.gitattributes 
new/morecvutils-1.1.0/.gitattributes
--- old/morecvutils-1.0.2/.gitattributes        2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/.gitattributes        1970-01-01 01:00:00.000000000 
+0100
@@ -1,20 +0,0 @@
-.gitattributes text eol=lf
-.gitignore text eol=lf
-Makefile text eol=lf
-*.yml text eol=lf
-LICENSE text eol=lf
-*.ipynb text eol=lf
-*.txt text eol=lf
-*.py text eol=lf
-*.sh text eol=lf
-*.c text eol=lf
-*.cpp text eol=lf
-*.f text eol=lf
-*.for text eol=lf
-*.f90 text eol=lf
-*.md text eol=lf
-*.rst text eol=lf
-*.csv text eol=lf
-*.m text eol=lf
-*.grc text eol=lf
-*.pas text eol=lf
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.github/FUNDING.yml 
new/morecvutils-1.1.0/.github/FUNDING.yml
--- old/morecvutils-1.0.2/.github/FUNDING.yml   2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/.github/FUNDING.yml   1970-01-01 01:00:00.000000000 
+0100
@@ -1,4 +0,0 @@
-# These are supported funding model platforms
-
-github: [scivision]
-ko_fi: scivision
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.github/workflows/ci_python.yml 
new/morecvutils-1.1.0/.github/workflows/ci_python.yml
--- old/morecvutils-1.0.2/.github/workflows/ci_python.yml       2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/.github/workflows/ci_python.yml       2026-03-11 
23:03:53.000000000 +0100
@@ -6,21 +6,19 @@
       - "**.py"
       - ".github/workflows/ci_python.yml"
   pull_request:
-    paths:
-      - "**.py"
-      - ".github/workflows/ci_python.yml"
 
 jobs:
 
   linux:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v1
-    - uses: actions/setup-python@v1
+    - uses: actions/checkout@v6
+    - uses: actions/setup-python@v6
       with:
-        python-version: '3.7'
-    - run: pip install .[tests,lint,opencv]
-    - run: flake8
-    - run: mypy .
+        python-version: '3.10'
+
+    - run: pip install .[tests,opencv]
+
+    - run: mypy --install-types --non-interactive
+
     - run: pytest
-      working-directory: tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/morecvutils-1.0.2/.github/workflows/publish-python-package.yml 
new/morecvutils-1.1.0/.github/workflows/publish-python-package.yml
--- old/morecvutils-1.0.2/.github/workflows/publish-python-package.yml  
1970-01-01 01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/.github/workflows/publish-python-package.yml  
2026-03-11 23:03:53.000000000 +0100
@@ -0,0 +1,34 @@
+# https://docs.pypi.org/trusted-publishers/using-a-publisher/
+
+name: publish
+
+on:
+  release:
+    types: [published]
+
+jobs:
+  release:
+
+    runs-on: ubuntu-latest
+
+    environment: release
+
+    permissions:
+      id-token: write
+
+    steps:
+    - uses: actions/checkout@v5
+
+    - name: Set up Python
+      uses: actions/setup-python@v6
+      with:
+        python-version: '3.x'
+
+    - name: Install builder
+      run: pip install build
+
+    - name: Build package
+      run: python -m build
+
+    - name: Publish package
+      uses: pypa/gh-action-pypi-publish@release/v1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/.pre-commit-config.yaml 
new/morecvutils-1.1.0/.pre-commit-config.yaml
--- old/morecvutils-1.0.2/.pre-commit-config.yaml       1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/.pre-commit-config.yaml       2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1,28 @@
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v6.0.0
+    hooks:
+      - id: trailing-whitespace
+      - id: end-of-file-fixer
+      - id: mixed-line-ending
+        args: [--fix=lf]
+      - id: check-yaml
+      - id: check-toml
+      - id: check-json
+      - id: check-case-conflict
+      - id: check-illegal-windows-names
+      - id: destroyed-symlinks
+
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.15.5
+    hooks:
+    # Run the linter.
+    - id: ruff-check
+      args: [ --fix ]
+    # Run the formatter.
+    - id: ruff-format
+
+  - repo: https://github.com/pre-commit/mirrors-mypy
+    rev: v1.19.1
+    hooks:
+      - id: mypy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/DemoConnectedComponentsBlob.py 
new/morecvutils-1.1.0/DemoConnectedComponentsBlob.py
--- old/morecvutils-1.0.2/DemoConnectedComponentsBlob.py        2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/DemoConnectedComponentsBlob.py        2026-03-11 
23:03:53.000000000 +0100
@@ -6,7 +6,7 @@
 from morecvutils.connectedComponents import setupblob, doblob
 
 
-def gen_patterns(x: int, y: int, dtype=np.uint8, add_noise: float = 0.0) -> 
np.ndarray:
+def gen_patterns(x: int, y: int, dtype=np.uint8, add_noise: float = 0.0):
 
     if dtype == np.uint8:
         V = 255
@@ -34,7 +34,7 @@
     im[190:220:1, 120:200:1] = V
 
     if add_noise > 0:
-        im = random_noise(im, 's&p', amount=add_noise).astype('uint8') * V
+        im = random_noise(im, "s&p", amount=add_noise).astype("uint8") * V
 
     return im
 
@@ -45,13 +45,13 @@
     x = y = 256
 
     img_area = x * y
-    print(f'area of the {x} x {y} image is {img_area} pixels')
+    print(f"area of the {x} x {y} image is {img_area} pixels")
 
     # set some arbitrary parameters
     max_blob = img_area // 4
     min_blob = img_area // 1000
 
-    print(f'Minimum and maximum blob areas: {min_blob}, {max_blob}')
+    print(f"Minimum and maximum blob areas: {min_blob}, {max_blob}")
 
     im1 = gen_patterns(x, y, np.uint8, 0.0)
 
@@ -59,11 +59,11 @@
 
     labeled_img, label_sizes = doblob(im1, blob)
 
-    cv2.imshow('Labeled Image', labeled_img)
-    print('press any key to exit')
+    cv2.imshow("Labeled Image", labeled_img)
+    print("press any key to exit")
     cv2.waitKey(0)
     cv2.destroyAllWindows()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/DemoMedianFilter.py 
new/morecvutils-1.1.0/DemoMedianFilter.py
--- old/morecvutils-1.0.2/DemoMedianFilter.py   2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/DemoMedianFilter.py   2026-03-11 23:03:53.000000000 
+0100
@@ -3,12 +3,9 @@
 import numpy as np
 from skimage.util import random_noise
 from matplotlib.pyplot import figure, show
-from typing import Tuple
 
 
-def gen_patterns(
-    x: int, y: int, dtype=np.uint8, noise: float = 0.0
-) -> Tuple[np.ndarray, np.ndarray]:
+def gen_patterns(x: int, y: int, dtype=np.uint8, noise: float = 0.0) -> tuple:
 
     if dtype == np.uint8:
         V = 255
@@ -34,38 +31,38 @@
     im[21:24:2, 21:24:2] = V
 
     if noise > 0:
-        im = random_noise(im, 's&p', amount=noise).astype('uint8') * V
+        im = random_noise(im, "s&p", amount=noise).astype("uint8") * V
 
-    im2 = np.zeros((y, x), dtype='uint8')
+    im2 = np.zeros((y, x), dtype="uint8")
     im2[4:7, 4:7] = V
     im2[4, 8] = V
 
     return im, im2
 
 
-def plot_panel(fg, im: np.ndarray):
+def plot_panel(fg, im):
     ax = fg.add_subplot(1, 4, 1)
-    ax.imshow(im, cmap='gray_r', interpolation='none', origin='bottom')
-    ax.set_title('original')
+    ax.imshow(im, cmap="gray_r", interpolation="none", origin="bottom")
+    ax.set_title("original")
 
     imfilt = cv2.medianBlur(im, 3)
 
     ax = fg.add_subplot(1, 4, 2)
-    ax.imshow(imfilt, cmap='gray_r', interpolation='none', origin='bottom')
-    ax.set_title('median filtered')
+    ax.imshow(imfilt, cmap="gray_r", interpolation="none", origin="bottom")
+    ax.set_title("median filtered")
 
     openrad = 3
     kern = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (openrad, openrad))
 
     ax = fg.add_subplot(1, 4, 3)
-    ax.imshow(cv2.erode(im, kern), cmap='gray_r', interpolation='none', 
origin='bottom')
-    ax.set_title('erosion')
+    ax.imshow(cv2.erode(im, kern), cmap="gray_r", interpolation="none", 
origin="bottom")
+    ax.set_title("erosion")
 
     ax = fg.add_subplot(1, 4, 4)
     ax.imshow(
-        cv2.erode(imfilt, kern), cmap='gray_r', interpolation='none', 
origin='bottom'
+        cv2.erode(imfilt, kern), cmap="gray_r", interpolation="none", 
origin="bottom"
     )
-    ax.set_title('erosion median filtered')
+    ax.set_title("erosion median filtered")
 
     # for a in ax:
     #   a.set_xlim((0, im.shape[1]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/DemoOptFlow.m 
new/morecvutils-1.1.0/DemoOptFlow.m
--- old/morecvutils-1.0.2/DemoOptFlow.m 2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/DemoOptFlow.m 2026-03-11 23:03:53.000000000 +0100
@@ -1,7 +1,5 @@
-function flows = test_optflow(I)
-if verLessThan('matlab','R2015a')
-    error('this Python Matlab Engine demo requires Matlab >= R2015a')
-end
+function flows = DemoOptFlow(I)
+
 if nargin==0
     I = genuint8();
 end
@@ -13,7 +11,7 @@
 flows = nan(size(I),'single');
 
 for i = 1:size(I,1)
-    
+
     F = estimateFlow(of, squeeze(I(i,:,:)));
     flows(i,:,:) = F.Magnitude;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/LICENSE.txt 
new/morecvutils-1.1.0/LICENSE.txt
--- old/morecvutils-1.0.2/LICENSE.txt   2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/LICENSE.txt   2026-03-11 23:03:53.000000000 +0100
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014-2017 Michael Hirsch, Ph.D.
+Copyright (c) 2014-2017 SciVision
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/MANIFEST.in 
new/morecvutils-1.1.0/MANIFEST.in
--- old/morecvutils-1.0.2/MANIFEST.in   2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/MANIFEST.in   2026-03-11 23:03:53.000000000 +0100
@@ -1,2 +1,2 @@
 include LICENSE.txt
-recursive-include tests *.py *.png
\ No newline at end of file
+recursive-include tests *.py *.png
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/OpticalFlow_Python_vs_Matlab.py 
new/morecvutils-1.1.0/OpticalFlow_Python_vs_Matlab.py
--- old/morecvutils-1.0.2/OpticalFlow_Python_vs_Matlab.py       2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/OpticalFlow_Python_vs_Matlab.py       2026-03-11 
23:03:53.000000000 +0100
@@ -1,7 +1,6 @@
 #!/usr/bin/env python
 from pathlib import Path
 import imageio
-from typing import Sequence
 import numpy as np
 from matplotlib.pyplot import figure, draw, pause, show
 
@@ -9,39 +8,39 @@
 from morecvutils.calcOptFlow import setupuv, optflowHornSchunk
 
 
-def plotflow(mag, matmag):
+def plotflow(im, mag, matmag):
     fg = figure(figsize=(12, 5))
     ax = fg.subplots(1, 2)
 
-    for i in range(I.shape[0] - 1):
+    for i in range(im.shape[0] - 1):
         if i == 0:
             hi = ax[0].imshow(mag[0, ...])
-            ax[0].set_title('Python optical flow mag')
+            ax[0].set_title("Python optical flow mag")
             fg.colorbar(hi, ax=ax[0])
 
             if matmag is not None:
                 hm = ax[1].imshow(matmag[0, ...])
-                ax[1].set_title('Matlab Optical flow mag')
+                ax[1].set_title("Matlab Optical flow mag")
                 fg.colorbar(hm, ax=ax[1])
 
-            hs = fg.suptitle('frame #')
+            hs = fg.suptitle("frame #")
         else:
             hi.set_data(mag[i, ...])
             if matmag is not None:
                 hm.set_data(matmag[i, ...])
 
-            hs.set_text('frame # {}'.format(i))
+            hs.set_text("frame # {}".format(i))
 
         draw()
         pause(0.001)
 
 
-def matlab_flow(I):
+def matlab_flow(im):
     import matlab.engine
 
     eng = matlab.engine.start_matlab("-nojvm")
 
-    matmag = eng.test_optflow(matlab.uint8(I.tolist()))
+    matmag = eng.test_optflow(matlab.uint8(im.tolist()))
     matmag = np.asarray(matmag)
 
     eng.quit()
@@ -49,19 +48,19 @@
     return matmag
 
 
-def py_flow(I, N, r, c):
+def py_flow(im, N, r, c):
     uv = setupuv((r, c))
 
     mag = np.empty((N, r, c))  # priming read
 
     for i in range(N):
-        flow = optflowHornSchunk(I[i + 1, ...], I[i, ...], uv, smoothing=0.001)
+        flow = optflowHornSchunk(im[i + 1, ...], im[i, ...], uv, 
smoothing=0.001)
         mag[i, ...] = np.hypot(flow[..., 0], flow[..., 1])
 
     return mag
 
 
-def setup(flist: Sequence[Path]):
+def setup(flist: list[Path]):
     N = len(flist) - 1
     r, c = imageio.imread(str(flist[0])).shape
     im = np.empty((N + 1, r, c), dtype=np.uint8)
@@ -72,19 +71,18 @@
     return im, N, r, c
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
+    flist = sorted(Path("tests/data").glob("*.png"))
 
-    flist = sorted(Path('tests/data').glob('*.png'))
-
-    I, N, r, c = setup(flist)
+    im, N, r, c = setup(flist)
     # %% python
-    mag = py_flow(I, N, r, c)
+    mag = py_flow(im, N, r, c)
     # %% Matlab
     try:
-        matmag = matlab_flow(I)
+        matmag = matlab_flow(im)
     except ImportError:
         matmag = None
     # %%
-    plotflow(mag, matmag)
+    plotflow(im, mag, matmag)
 
     show()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/archive/.travis.yml 
new/morecvutils-1.1.0/archive/.travis.yml
--- old/morecvutils-1.0.2/archive/.travis.yml   2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/archive/.travis.yml   1970-01-01 01:00:00.000000000 
+0100
@@ -1,25 +0,0 @@
-language: python
-group: travis_latest
-
-git:
-  depth: 25
-  quiet: true
-
-python:
-- 3.6
-
-os:
-- linux
-
-install:
-- pip install -e .[tests,cov,opencv]
-
-script:
-- flake8
-- mypy .
-- pytest -rsv
-
-
-after_success:
-- pytest --cov
-- coveralls
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/morecvutils/calcOptFlow.py 
new/morecvutils-1.1.0/morecvutils/calcOptFlow.py
--- old/morecvutils-1.0.2/morecvutils/calcOptFlow.py    2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/morecvutils/calcOptFlow.py    1970-01-01 
01:00:00.000000000 +0100
@@ -1,65 +0,0 @@
-#!/usr/bin/env python
-"""
-Michael Hirsch
-Example calculations of optical flow, starting with Horn Schunk Optical Flow 
using OpenCV
-"""
-import numpy as np
-
-try:
-    import cv2
-except ImportError:  # NOTE: openCV 3 has legacy code buried in opencv-extra
-    cv2 = None
-from numpy import asarray, dstack
-
-#
-from pyoptflow import HornSchunck
-
-
-def optflowHornSchunk(
-    new: np.ndarray, ref: np.ndarray, uv, smoothing=0.01
-) -> np.ndarray:
-    if cv2 is not None:
-        """
-        http://docs.opencv.org/modules/legacy/doc/motion_analysis.html
-        Note that smoothness parameter for cv.CalcOpticalFlowHS needs to be 
SMALLER than matlab
-        to get similar result. Useless when smoothness was 1 in OpenCV, but 
it's 1 in Matlab!
-        """
-        cvref = cv2.fromarray(ref)
-        cvnew = cv2.fromarray(new)
-        # result is placed in u,v
-        # matlab vision.OpticalFlow Horn-Shunck has default maxiter=10, 
terminate=eps, smoothness=1
-        cv2.CalcOpticalFlowHS(
-            cvref,
-            cvnew,
-            False,
-            uv[0],
-            uv[1],
-            smoothing,
-            (cv2.CV_TERMCRIT_ITER | cv2.CV_TERMCRIT_EPS, 8, 0.1),
-        )
-
-        # reshape to numpy float32, xpix x ypix x 2
-        flow = dstack((asarray(uv[0]), asarray(uv[1])))
-    else:  # use Python method
-        u, v = HornSchunck(ref, new)
-        flow = dstack((u, v))
-
-    return flow
-
-
-def setupuv(rc):
-    """
-    Horn Schunck legacy OpenCV function requires we use these old-fashioned cv 
matrices, not numpy array
-    """
-    if cv2 is not None:
-        (r, c) = rc
-        u = cv2.CreateMat(r, c, cv2.CV_32FC1)
-        v = cv2.CreateMat(r, c, cv2.CV_32FC1)
-        return (u, v)
-    else:
-        return [None] * 2
-
-
-def calcofhs(new, ref, smoothing):
-    uv = setupuv(new.shape)
-    return optflowHornSchunk(new, ref, uv, smoothing)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/morecvutils/connectedComponents.py 
new/morecvutils-1.1.0/morecvutils/connectedComponents.py
--- old/morecvutils-1.0.2/morecvutils/connectedComponents.py    2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/morecvutils/connectedComponents.py    1970-01-01 
01:00:00.000000000 +0100
@@ -1,50 +0,0 @@
-import cv2
-import numpy as np
-from typing import Tuple
-
-
-def doblob(
-    img: np.ndarray, blobdet, anno: bool = True
-) -> Tuple[np.ndarray, np.ndarray]:
-    """
-    img: can be RGB (MxNx3) or gray (MxN)
-
-    
http://docs.opencv.org/master/modules/features2d/doc/drawing_function_of_keypoints_and_matches.html
-    
http://docs.opencv.org/trunk/modules/features2d/doc/drawing_function_of_keypoints_and_matches.html
-    """
-    keypoints = blobdet.detect(img)
-    kpsize = np.asarray([k.size for k in keypoints])
-    final = img.copy()  # is the .copy necessary?
-
-    final = cv2.drawKeypoints(
-        img, keypoints, outImage=final, 
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
-    )
-    # %% plot count of blobs
-    if anno:
-        cv2.putText(
-            final,
-            text=str(len(keypoints)),
-            org=(int(img.shape[1] * 0.9), 25),
-            fontFace=cv2.FONT_HERSHEY_PLAIN,
-            fontScale=2,
-            color=(0, 255, 0),
-            thickness=2,
-        )
-
-    return final, kpsize
-
-
-def setupblob(minarea: int, maxarea: int, mindist: int):
-
-    blobparam = cv2.SimpleBlobDetector_Params()
-    blobparam.filterByArea = True
-    blobparam.filterByColor = False
-    blobparam.filterByCircularity = False
-    blobparam.filterByInertia = False
-    blobparam.filterByConvexity = False
-
-    blobparam.minDistBetweenBlobs = mindist
-    blobparam.minArea = minarea
-    blobparam.maxArea = maxarea
-    # blobparam.minThreshold = 40 #we have already made a binary image
-    return cv2.SimpleBlobDetector_create(blobparam)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/morecvutils/cv2draw.py 
new/morecvutils-1.1.0/morecvutils/cv2draw.py
--- old/morecvutils-1.0.2/morecvutils/cv2draw.py        2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/morecvutils/cv2draw.py        1970-01-01 
01:00:00.000000000 +0100
@@ -1,89 +0,0 @@
-from __future__ import division, absolute_import
-import cv2
-from numpy import (
-    dstack,
-    degrees,
-    pi,
-    array,
-    ones_like,
-    arctan2,
-    hypot,
-    mgrid,
-    vstack,
-    uint8,
-    int32,
-    iinfo,
-)
-
-
-def draw_flow(img, flow, step=16, dtype=uint8):
-    """
-    draws flow vectors on image
-    this came from opencv/examples directory
-    another way: 
http://docs.opencv.org/trunk/doc/py_tutorials/py_gui/py_drawing_functions/py_drawing_functions.html
-    """
-    maxval = iinfo(img.dtype).max
-
-    # scaleFact = 1. #arbitary factor to make flow visible
-    canno = (0, maxval, 0)  # green color
-    h, w = img.shape[:2]
-    y, x = mgrid[step // 2: h: step, step // 2: w: step].reshape(2, -1)
-    fx, fy = flow[y, x].T
-    # create line endpoints
-    lines = vstack([x, y, (x + fx), (y + fy)]).T.reshape(-1, 2, 2)
-    lines = int32(lines + 0.5)
-    # create image
-    if img.ndim == 2:  # assume gray
-        vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
-    else:  # already RGB
-        vis = img
-    # draw line
-    cv2.polylines(vis, lines, isClosed=False, color=canno, thickness=1, 
lineType=8)
-    # draw filled green circles
-    for (x1, y1), (_x2, _y2) in lines:
-        cv2.circle(vis, center=(x1, y1), radius=1, color=canno, thickness=-1)
-    return vis
-
-
-def draw_hsv(mag, ang, dtype=uint8, fn=None):
-    """
-    mag must be uint8, uint16, uint32 and 2-D
-    ang is in radians (float)
-    """
-    assert mag.shape == ang.shape
-    assert mag.ndim == 2
-    maxval = iinfo(dtype).max
-
-    hsv = dstack(
-        (
-            (degrees(ang) / 2).astype(dtype),  # /2 to keep less than 255
-            ones_like(mag) * maxval,  # maxval must be after in 1-D case
-            cv2.normalize(mag, alpha=0, beta=maxval, 
norm_type=cv2.NORM_MINMAX),
-        )
-    )
-    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
-
-    if fn is not None:
-        print('writing ' + fn)
-        cv2.imwrite(fn, rgb)
-
-    return rgb  # , hsv
-
-
-def flow2magang(flow, dtype=uint8):
-    """
-    flow dimensions y,x,2  3-D.  flow[...,0] is magnitude, flow[...,1] is angle
-    """
-    fx, fy = flow[..., 0], flow[..., 1]
-    return hypot(fx, fy).astype(dtype), arctan2(fy, fx) + pi
-
-
-# %% selftest
-if __name__ == '__main__':
-    flow = array([[[55, pi / 4], [128, 3 * pi / 2]], [[123, pi / 2], [48, 
pi]]])
-
-    mag, ang = flow2magang(flow, uint8)
-
-    rgb = draw_hsv(mag, ang)
-
-    assert rgb[1, 0, 2] == 239
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/morecvutils/getaviprop.py 
new/morecvutils-1.1.0/morecvutils/getaviprop.py
--- old/morecvutils-1.0.2/morecvutils/getaviprop.py     2019-11-19 
20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/morecvutils/getaviprop.py     1970-01-01 
01:00:00.000000000 +0100
@@ -1,60 +0,0 @@
-#!/usr/bin/env python
-"""
-gets basic info about AVI file using OpenCV
-
-input: filename or cv2.Capture
-"""
-from pathlib import Path
-from struct import pack
-from typing import Dict, Any
-import cv2
-
-
-def getaviprop(fn: Path) -> Dict[str, Any]:
-    if isinstance(fn, (str, Path)):  # assuming filename
-        fn = Path(fn).expanduser()
-        if not fn.is_file():
-            raise FileNotFoundError(fn)
-        v = cv2.VideoCapture(str(fn))
-        if v is None:
-            raise OSError(f'could not read {fn}')
-    else:  # assuming cv2.VideoCapture object
-        v = fn
-
-    if not v.isOpened():
-        raise OSError(f'cannot read {fn}  probable codec issue')
-
-    vidparam = {
-        'nframe': int(v.get(cv2.CAP_PROP_FRAME_COUNT)),
-        'xy_pixel': (
-            int(v.get(cv2.CAP_PROP_FRAME_WIDTH)),
-            int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)),
-        ),
-        'fps': v.get(cv2.CAP_PROP_FPS),
-        'codec': fourccint2ascii(int(v.get(cv2.CAP_PROP_FOURCC))),
-    }
-
-    if isinstance(fn, Path):
-        v.release()
-
-    return vidparam
-
-
-def fourccint2ascii(fourcc_int: int) -> str:
-    """
-    convert fourcc 32-bit integer code to ASCII
-    """
-    assert isinstance(fourcc_int, int)
-
-    return pack('<I', fourcc_int).decode('ascii')
-
-
-if __name__ == '__main__':
-    from argparse import ArgumentParser
-
-    p = ArgumentParser(description='get parameters of AVI file')
-    p.add_argument('avifn', help='avi filename')
-    p = p.parse_args()
-
-    vidparam = getaviprop(p.avifn)
-    print(vidparam)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/mypy.ini 
new/morecvutils-1.1.0/mypy.ini
--- old/morecvutils-1.0.2/mypy.ini      2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/mypy.ini      1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-[mypy]
-ignore_missing_imports = True
-strict_optional = False
-allow_redefinition = True
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/pyproject.toml 
new/morecvutils-1.1.0/pyproject.toml
--- old/morecvutils-1.0.2/pyproject.toml        2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/pyproject.toml        2026-03-11 23:03:53.000000000 
+0100
@@ -1,2 +1,37 @@
 [build-system]
-requires = ["setuptools", "wheel"]
\ No newline at end of file
+requires = ["setuptools>=61.0.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "morecvutils"
+description = "Computer Vision utilities, OpenCV plot helpers for Optical Flow 
and Blob Analysis, AVI codec helpers"
+requires-python = ">=3.10"
+keywords = ["computer vision", "opencv"]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Environment :: Console",
+    "Intended Audience :: Science/Research",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3",
+    "Topic :: Scientific/Engineering :: Visualization"
+]
+dynamic = ["readme", "version"]
+dependencies = [
+    "numpy",
+    "imageio"
+]
+
+[tool.setuptools.dynamic]
+readme = {file = ["README.md"], content-type = "text/markdown"}
+version = {attr = "morecvutils.__version__"}
+
+[project.optional-dependencies]
+tests = ["pytest", "mypy"]
+plot = ["matplotlib"]
+opencv = ["opencv-python"]
+
+[tool.mypy]
+files = ["."]
+install_types = true
+exclude = ["^build([/\\\\]|$)"]
+ignore_missing_imports = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/pytest.ini 
new/morecvutils-1.1.0/pytest.ini
--- old/morecvutils-1.0.2/pytest.ini    2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/pytest.ini    1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-[pytest]
-addopts = -ra -v
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/setup.cfg 
new/morecvutils-1.1.0/setup.cfg
--- old/morecvutils-1.0.2/setup.cfg     2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/setup.cfg     1970-01-01 01:00:00.000000000 +0100
@@ -1,49 +0,0 @@
-[metadata]
-name = morecvutils
-version = 1.0.2
-author = Michael Hirsch, Ph.D.
-author_email = [email protected]
-description = Computer Vision utilities, OpenCV plot helpers for Optical Flow 
and Blob Analysis, AVI codec helpers
-url = https://github.com/scivision/morecvutils
-keywords =
-  computer vision
-  opencv
-classifiers =
-  Development Status :: 5 - Production/Stable
-  Environment :: Console
-  Intended Audience :: Science/Research
-  Operating System :: OS Independent
-  Programming Language :: Python :: 3.6
-  Programming Language :: Python :: 3.7
-  Programming Language :: Python :: 3.8
-  Topic :: Scientific/Engineering :: Visualization
-license_files =
-  LICENSE.txt
-long_description = file: README.md
-long_description_content_type = text/markdown
-
-[options]
-python_requires = >= 3.6
-packages = find:
-include_package_data = True
-zip_safe = False
-scripts =
-  DemoOptFlow.m
-  DemoConnectedComponentsBlob.py
-  DemoMedianFilter.py
-  OpticalFlow_Python_vs_Matlab.py
-install_requires =
-  numpy
-  imageio
-
-[options.extras_require]
-tests =
-  pytest
-lint =
-  flake8
-  mypy
-plot =
-  matplotlib
-  seaborn
-opencv =
-  opencv-python
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/setup.py 
new/morecvutils-1.1.0/setup.py
--- old/morecvutils-1.0.2/setup.py      2019-11-19 20:00:50.000000000 +0100
+++ new/morecvutils-1.1.0/setup.py      1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-#!/usr/bin/env python
-from setuptools import setup
-
-setup()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/src/morecvutils/__init__.py 
new/morecvutils-1.1.0/src/morecvutils/__init__.py
--- old/morecvutils-1.0.2/src/morecvutils/__init__.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/__init__.py   2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1 @@
+__version__ = "1.1.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/src/morecvutils/calcOptFlow.py 
new/morecvutils-1.1.0/src/morecvutils/calcOptFlow.py
--- old/morecvutils-1.0.2/src/morecvutils/calcOptFlow.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/calcOptFlow.py        2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+"""
+Michael Hirsch
+Example calculations of optical flow, starting with Horn Schunk Optical Flow 
using OpenCV
+"""
+
+import numpy as np
+
+from pyoptflow import HornSchunck
+
+
+def optflowHornSchunk(new, ref, uv, smoothing=0.01):
+    try:
+        import cv2
+
+        """
+        http://docs.opencv.org/modules/legacy/doc/motion_analysis.html
+        Note that smoothness parameter for cv.CalcOpticalFlowHS needs to be 
SMALLER than matlab
+        to get similar result. Useless when smoothness was 1 in OpenCV, but 
it's 1 in Matlab!
+        """
+        cvref = cv2.fromarray(ref)
+        cvnew = cv2.fromarray(new)
+        # result is placed in u,v
+        # matlab vision.OpticalFlow Horn-Shunck has default maxiter=10, 
terminate=eps, smoothness=1
+        cv2.CalcOpticalFlowHS(
+            cvref,
+            cvnew,
+            False,
+            uv[0],
+            uv[1],
+            smoothing,
+            (cv2.CV_TERMCRIT_ITER | cv2.CV_TERMCRIT_EPS, 8, 0.1),
+        )
+
+        # reshape to numpy float32, xpix x ypix x 2
+        flow = np.dstack((np.asarray(uv[0]), np.asarray(uv[1])))
+    except ImportError:
+        u, v = HornSchunck(ref, new)
+        flow = np.dstack((u, v))
+
+    return flow
+
+
+def setupuv(rc):
+    """
+    Horn Schunck legacy OpenCV function requires we use these old-fashioned cv 
matrices, not numpy array
+    """
+    if cv2 is not None:
+        (r, c) = rc
+        u = cv2.CreateMat(r, c, cv2.CV_32FC1)
+        v = cv2.CreateMat(r, c, cv2.CV_32FC1)
+        return (u, v)
+    else:
+        return [None] * 2
+
+
+def calcofhs(new, ref, smoothing):
+    uv = setupuv(new.shape)
+    return optflowHornSchunk(new, ref, uv, smoothing)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/morecvutils-1.0.2/src/morecvutils/connectedComponents.py 
new/morecvutils-1.1.0/src/morecvutils/connectedComponents.py
--- old/morecvutils-1.0.2/src/morecvutils/connectedComponents.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/connectedComponents.py        
2026-03-11 23:03:53.000000000 +0100
@@ -0,0 +1,47 @@
+import cv2
+import numpy as np
+
+
+def doblob(img, blobdet, anno: bool = True) -> tuple:
+    """
+    img: can be RGB (MxNx3) or gray (MxN)
+
+    
http://docs.opencv.org/master/modules/features2d/doc/drawing_function_of_keypoints_and_matches.html
+    
http://docs.opencv.org/trunk/modules/features2d/doc/drawing_function_of_keypoints_and_matches.html
+    """
+    keypoints = blobdet.detect(img)
+    kpsize = np.asarray([k.size for k in keypoints])
+    final = img.copy()  # is the .copy necessary?
+
+    final = cv2.drawKeypoints(
+        img, keypoints, outImage=final, 
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
+    )
+    # %% plot count of blobs
+    if anno:
+        cv2.putText(
+            final,
+            text=str(len(keypoints)),
+            org=(int(img.shape[1] * 0.9), 25),
+            fontFace=cv2.FONT_HERSHEY_PLAIN,
+            fontScale=2,
+            color=(0, 255, 0),
+            thickness=2,
+        )
+
+    return final, kpsize
+
+
+def setupblob(minarea: int, maxarea: int, mindist: int):
+
+    blobparam = cv2.SimpleBlobDetector_Params()  # type: ignore[attr-defined]
+    blobparam.filterByArea = True
+    blobparam.filterByColor = False
+    blobparam.filterByCircularity = False
+    blobparam.filterByInertia = False
+    blobparam.filterByConvexity = False
+
+    blobparam.minDistBetweenBlobs = mindist
+    blobparam.minArea = minarea
+    blobparam.maxArea = maxarea
+    # blobparam.minThreshold = 40 #we have already made a binary image
+    return cv2.SimpleBlobDetector_create(blobparam)  # type: 
ignore[attr-defined]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/src/morecvutils/cv2draw.py 
new/morecvutils-1.1.0/src/morecvutils/cv2draw.py
--- old/morecvutils-1.0.2/src/morecvutils/cv2draw.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/cv2draw.py    2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1,78 @@
+import cv2
+
+import numpy as np
+
+
+def draw_flow(img, flow, step=16):
+    """
+    draws flow vectors on image
+    this came from opencv/examples directory
+    another way: 
http://docs.opencv.org/trunk/doc/py_tutorials/py_gui/py_drawing_functions/py_drawing_functions.html
+    """
+    maxval = np.iinfo(img.dtype).max
+
+    # scaleFact = 1. #arbitary factor to make flow visible
+    canno = (0, maxval, 0)  # green color
+    h, w = img.shape[:2]
+    y, x = np.mgrid[step // 2 : h : step, step // 2 : w : step].reshape(2, -1)
+    fx, fy = flow[y, x].T
+    # create line endpoints
+    lines = np.vstack([x, y, (x + fx), (y + fy)]).transpose.reshape(-1, 2, 2)
+    lines = np.int32(lines + 0.5)
+    # create image
+    if img.ndim == 2:  # assume gray
+        vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
+    else:  # already RGB
+        vis = img
+    # draw line
+    cv2.polylines(vis, lines, isClosed=False, color=canno, thickness=1, 
lineType=8)
+    # draw filled green circles
+    for (x1, y1), (_x2, _y2) in lines:
+        cv2.circle(vis, center=(x1, y1), radius=1, color=canno, thickness=-1)
+    return vis
+
+
+def draw_hsv(mag, ang, dtype=np.uint8, fn: str | None = None):
+    """
+    mag must be uint8, uint16, uint32 and 2-D
+    ang is in radians (float)
+    """
+    assert mag.shape == ang.shape
+    assert mag.ndim == 2
+    maxval = np.iinfo(dtype).max
+
+    hsv = np.dstack(
+        (
+            (np.degrees(ang) / 2).astype(dtype),  # /2 to keep less than 255
+            np.ones_like(mag) * maxval,  # maxval must be after in 1-D case
+            cv2.normalize(mag, alpha=0, beta=maxval, 
norm_type=cv2.NORM_MINMAX),  # type: ignore[call-overload]
+        )
+    )
+    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
+
+    if fn is not None:
+        print("writing " + fn)
+        cv2.imwrite(fn, rgb)
+
+    return rgb  # , hsv
+
+
+def flow2magang(flow, dtype=np.uint8):
+    """
+    flow dimensions y,x,2  3-D.  flow[...,0] is magnitude, flow[...,1] is angle
+    """
+    fx, fy = flow[..., 0], flow[..., 1]
+    return np.hypot(fx, fy).astype(dtype), np.arctan2(fy, fx) + np.pi
+
+
+# %% selftest
+if __name__ == "__main__":
+    flow = np.array(
+        [[[55, np.pi / 4], [128, 3 * np.pi / 2]], [[123, np.pi / 2], [48, 
np.pi]]]
+    )
+
+    mag, ang = flow2magang(flow, np.uint8)
+
+    rgb = draw_hsv(mag, ang)
+
+    assert rgb[1, 0, 2] == 239
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/src/morecvutils/getaviprop.py 
new/morecvutils-1.1.0/src/morecvutils/getaviprop.py
--- old/morecvutils-1.0.2/src/morecvutils/getaviprop.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/getaviprop.py 2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+"""
+gets basic info about AVI file using OpenCV
+
+input: filename or cv2.Capture
+"""
+
+from pathlib import Path
+from struct import pack
+from typing import Any
+import cv2
+
+
+def getaviprop(fn: str | Path) -> dict[str, Any]:
+    match fn:
+        case str() | Path():  # assuming filename
+            fn = Path(fn).expanduser()
+            if not fn.is_file():
+                raise FileNotFoundError(fn)
+            v = cv2.VideoCapture(str(fn))
+            if v is None:
+                raise OSError(f"could not read {fn}")
+        case _:  # assuming cv2.VideoCapture object
+            v = fn
+
+    if not v.isOpened():
+        raise OSError(f"cannot read {fn}  probable codec issue")
+
+    vidparam = {
+        "nframe": int(v.get(cv2.CAP_PROP_FRAME_COUNT)),
+        "xy_pixel": (
+            int(v.get(cv2.CAP_PROP_FRAME_WIDTH)),
+            int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)),
+        ),
+        "fps": v.get(cv2.CAP_PROP_FPS),
+        "codec": fourccint2ascii(int(v.get(cv2.CAP_PROP_FOURCC))),
+    }
+
+    if isinstance(fn, Path):
+        v.release()
+
+    return vidparam
+
+
+def fourccint2ascii(fourcc_int: int) -> str:
+    """
+    convert fourcc 32-bit integer code to ASCII
+    """
+    assert isinstance(fourcc_int, int)
+
+    return pack("<I", fourcc_int).decode("ascii")
+
+
+if __name__ == "__main__":
+    from argparse import ArgumentParser
+
+    p = ArgumentParser(description="get parameters of AVI file")
+    p.add_argument("avifn", help="avi filename")
+    args = p.parse_args()
+
+    vidparam = getaviprop(args.avifn)
+    print(vidparam)
Binary files old/morecvutils-1.0.2/src/morecvutils/tests/data/bunny.avi and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/bunny.avi differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_0.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_0.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_1.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_1.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_2.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_2.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_3.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_3.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_4.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_4.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_5.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_5.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_6.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_6.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_7.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_7.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_8.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_8.png 
differ
Binary files 
old/morecvutils-1.0.2/src/morecvutils/tests/data/vertsine_horizslide_9.png and 
new/morecvutils-1.1.0/src/morecvutils/tests/data/vertsine_horizslide_9.png 
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/src/morecvutils/tests/test_blob.py 
new/morecvutils-1.1.0/src/morecvutils/tests/test_blob.py
--- old/morecvutils-1.0.2/src/morecvutils/tests/test_blob.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/morecvutils-1.1.0/src/morecvutils/tests/test_blob.py    2026-03-11 
23:03:53.000000000 +0100
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+import pytest
+import numpy as np
+from pytest import approx
+
+import importlib.resources as ir
+
+
+def test_blob():
+    mb = pytest.importorskip("morecvutils.connectedComponents")
+    im_good = np.zeros((32, 32), dtype=np.uint8)
+    im_good[10:20, 10:20] = 255
+
+    im_bad = np.zeros((32, 32), dtype=np.uint8)
+    im_bad[10:12, 10:12] = 255
+
+    blob = mb.setupblob(10, 200, 4)
+
+    labeled_img, label_sizes = mb.doblob(im_good, blob)
+
+    assert labeled_img.shape == (32, 32, 3)
+    assert len(label_sizes) == 1
+
+    labeled_img, label_sizes = mb.doblob(im_bad, blob)
+
+    assert len(label_sizes) == 0
+
+
+def test_avi():
+    getaviprop = pytest.importorskip("morecvutils.getaviprop")
+
+    finf = getaviprop.getaviprop(ir.files("morecvutils.tests.data") / 
"bunny.avi")
+
+    assert finf["fps"] == approx(24.0)
+    assert finf["xy_pixel"] == (426, 240)
+    assert finf["nframe"] == 168
+    assert finf["codec"] == "h264"
Binary files old/morecvutils-1.0.2/tests/data/bunny.avi and 
new/morecvutils-1.1.0/tests/data/bunny.avi differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_0.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_0.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_1.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_1.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_2.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_2.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_3.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_3.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_4.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_4.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_5.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_5.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_6.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_6.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_7.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_7.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_8.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_8.png differ
Binary files old/morecvutils-1.0.2/tests/data/vertsine_horizslide_9.png and 
new/morecvutils-1.1.0/tests/data/vertsine_horizslide_9.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/morecvutils-1.0.2/tests/test_blob.py 
new/morecvutils-1.1.0/tests/test_blob.py
--- old/morecvutils-1.0.2/tests/test_blob.py    2019-11-19 20:00:50.000000000 
+0100
+++ new/morecvutils-1.1.0/tests/test_blob.py    1970-01-01 01:00:00.000000000 
+0100
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-import pytest
-import numpy as np
-from pytest import approx
-from pathlib import Path
-
-R = Path(__file__).parent
-
-
-def test_blob():
-    mb = pytest.importorskip('morecvutils.connectedComponents')
-    im_good = np.zeros((32, 32), dtype=np.uint8)
-    im_good[10:20, 10:20] = 255
-
-    im_bad = np.zeros((32, 32), dtype=np.uint8)
-    im_bad[10:12, 10:12] = 255
-
-    blob = mb.setupblob(10, 200, 4)
-
-    labeled_img, label_sizes = mb.doblob(im_good, blob)
-
-    assert labeled_img.shape == (32, 32, 3)
-    assert len(label_sizes) == 1
-
-    labeled_img, label_sizes = mb.doblob(im_bad, blob)
-
-    assert len(label_sizes) == 0
-
-
-def test_avi():
-    getaviprop = pytest.importorskip('morecvutils.getaviprop')
-
-    finf = getaviprop.getaviprop(R / 'data/bunny.avi')
-
-    assert finf['fps'] == approx(24.0)
-    assert finf['xy_pixel'] == (426, 240)
-    assert finf['nframe'] == 168
-    assert finf['codec'] == 'H264'
-
-
-if __name__ == '__main__':
-    pytest.main([__file__])

Reply via email to