image viewer in Python

From Noah.org
Jump to: navigation, search


This is a simple JPEG image viewer. It is intended for view bulk collections of images -- especially those single frame collections from a video. This tool allows you to play the images back as a video stream or to single step forward and backward through the images. You can also swap the direction of the video playback. This is very handy for browsing for individual frames. You can easily find a single frames and not worry about overshooting the mark. You can easily back up if you overshoot. Many standard video players have trouble backup up, so if you overshoot you have the jump backwards and then come against in the forward direction. There is no provision for single stepping frames backwards or playing the video backwards.

#!/usr/bin/env python

'''
SYNOPSIS

    vview [-h,--help] [-v,--verbose] [--version]

DESCRIPTION

    Start this program in the directory where you wish to browse JPEG images.
    The SPACE bar will start and stop video playback.
    The '.' key will single-step frome by frome.
    The 'r' key will reverse the direction of video playback (including single step.

EXIT STATUS

    TODO This exits with status 0 on success and 1 otherwise.

AUTHOR

    Noah Spurrier <noah@noah.org>

LICENSE

    This license is approved by the OSI and FSF as GPL-compatible.
        http://opensource.org/licenses/isc-license.txt

    Copyright (c) 2019, Noah Spurrier
    PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY
    PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE
    COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES.
    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

VERSION

    Version 1
'''

import cv2
import os, os.path

# This is a bit of an abuse of the iterator semantics.
# In this class the caller may choose to change direction midcourse through the iteration.
# The user may also choose to allow infinite iteration by turning off the StopIteration
# exception when the end of the array is reached. In this case there are two choices.
# The next iteration could cause the index to be fixed at the ends so that the same value we
# be returned foreve. The second choice is that the next iteration could loop around the the other
# end of the arrary. This streching of the meaning of iteration is not without precedent.
# The itertools module has a number of infinite iterators too choose from.
class ReversableArray:
    def __init__(self,indexible):
        self.array = indexible
        self.step = 1
        self.buf = []
        self.index = -1 * self.step
        self.last_index = len(self.array)
        # 0 for STOP, 1 for BOUNCE, 2 for LOOP. Any other value defaults to 0, STOP.
        self.deadend_disposition = 0
    def reverse(self):
        self.step = self.step * -1
    def __iter__(self): return self
    def next(self):
        return self.__next__()
    def __next__(self):
        self.index = self.index + self.step
        if self.index < 0 or self.index >= self.last_index:
            if self.deadend_disposition == 1: # BOUNCE
                self.index = self.index - self.step
            elif self.deadend_disposition == 2: # LOOP
                if self.index < 0:
                    self.index = self.last_index + self.index
                elif self.index >= self.last_index:
                    self.index = self.index - self.last_index
            else: # STOP
                raise StopIteration
        return self.array[self.index]

WindowName="Main View"
view_window =  cv2.namedWindow(WindowName, flags=cv2.CV_WINDOW_AUTOSIZE)

imageDir = "/Volumes/UNTITLED/"
image_path_list = [x for x in os.listdir(imageDir) if os.path.splitext(x)[1] == '.jpg']
print("Number Images: %d" % (len(image_path_list)))
print("")
print("Press . to step forward one frame.")
print("Press , to step back one frame.")
ra = ReversableArray(image_path_list)
ra.deadend_disposition=2
wait_key_delay = 0
for filename in ra:
    print("Filename: %s" % (filename))
    img = cv2.imread(os.path.join(imageDir, filename))
    if img is None:
        continue
    cv2.imshow(WindowName,img)
    key = cv2.waitKey(wait_key_delay)
    if key == ord(' '):
        if wait_key_delay == 0:
            wait_key_delay = 50
        else:
            wait_key_delay = 0
        continue
    if key == ord('.'):
        wait_key_delay=0
        continue
    if key == ord('r'):
        ra.reverse()
    if key == 27 or key == ord('q'):
        break

cv2.destroyAllWindows()