Codes » History » Revision 31
Revision 30 (Mitsuki EIKI, 01/16/2025 02:13 PM) → Revision 31/33 (Mitsuki EIKI, 01/16/2025 02:16 PM)
h1. Codes [[Wiki]] | [[About_Us]] | [[Project_Overview]] | [[UML_Diagram]] | [[Codes]] h1. main_stitcher.py Config Class Code <pre><code class="python"> #!/usr/bin/env python # -*- coding: utf-8 -*- from config import Config from img_processor import ImgProcessor from display_img import DisplayImg import cv2 class MainStitcher: """! @brief A class responsible for stitching and displaying images in a projection system. The class integrates configuration settings from the `Config` class and display properties from the `DisplayImg` class to handle the main stitching and display logic. """ def __init__(self, dImg): """! @brief Initializes the MainStitcher with configuration and display image settings. @param config An instance of the Config class containing projection configuration. @param dImg An instance of the DisplayImg class containing display settings. """ self.__displayImg = dImg #for single monitor/mirroring def singleDisplay(self): """! @brief use two laptops to project the final processed images. """ img = self.__displayImg.getImg() side = self.__displayImg.getSide() # cv2.namedWindow(side, cv2.WINDOW_NORMAL) cv2.imshow(side, img) # cv2.setWindowProperty(side, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) #for double extended monitor def doubleDisplay(self, monitorWidth): """! @brief use two laptops to project the final processed images. """ img = self.__displayImg.getImg() side = self.__displayImg.getSide() """ In moveWindow(name, x, y), we can replace x with the window size or something """ if(side == "Left"): cv2.namedWindow(side, cv2.WINDOW_NORMAL) cv2.moveWindow(side, 0, 0) cv2.imshow(side, img) cv2.resizeWindow(side, monitorWidth, 1080) cv2.setWindowProperty(side, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) elif(side == "Right"): cv2.namedWindow(side, cv2.WINDOW_NORMAL) cv2.moveWindow(side, monitorWidth, 0) cv2.imshow(side, img) cv2.resizeWindow(side, monitorWidth, 1080) cv2.setWindowProperty(side, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) def save(self): cv2.imwrite("./img/processed/" + self.__displayImg.getSide() + "_img.png", self.__displayImg.getImg()) def main(): config = Config.readConfigFile() imgProcessor = ImgProcessor(config) l_img, r_img = imgProcessor.cropImage() l_alpha_processed, r_alpha_processed = imgProcessor.alphaBlend(l_img, r_img) l_alpha_gamma_processed = imgProcessor.GammaCorrection(l_alpha_processed) r_alpha_gamma_processed = imgProcessor.GammaCorrection(r_alpha_processed) l_displayImg = DisplayImg(l_alpha_gamma_processed.shape[1] ,l_alpha_gamma_processed.shape[0], config.getOverlapWidth(), l_alpha_gamma_processed, "Left") r_displayImg = DisplayImg(r_alpha_gamma_processed.shape[1] ,r_alpha_gamma_processed.shape[0], config.getOverlapWidth(), r_alpha_gamma_processed, "Right") l_stitcher = MainStitcher(l_displayImg) r_stitcher = MainStitcher(r_displayImg) print(config.getIsDualMonitor()) if config.getIsDualMonitor(): l_stitcher.doubleDisplay(config.getMonitorWidth()) r_stitcher.doubleDisplay(config.getMonitorWidth()) else: if config.getSide().lower() == "right": r_stitcher.singleDisplay() else: l_stitcher.singleDisplay() cv2.waitKey(0) cv2.destroyAllWindows() l_stitcher.save() r_stitcher.save() if __name__ == "__main__": main() </code></pre> h1. config.py h2. config.py: Configuration Class <pre><code class="python"> #!/usr/bin/env python # -*- coding: utf-8 -*- from distutils.util import strtobool class Config(object): def __init__(self, pnd, prd, w, h, p, gamma, overlapWidth, side, isDual, monitorWidth): """! Constructor for Config class. @param pnd: Projection distance. @param prd: Projector distance. @param w: Image width. @param h: Image height. @param p: Path to the image. @param gamma: Gamma value for adjustments. @param overlapWidth: Width of the overlap area. @param side: Side of projection. """ self.__projection_distance = pnd self.__projector_diatance = prd self.__img_width = w self.__img_height = h self.__img_path = p self.__gamma = gamma self.__overlapWidth = overlapWidth self.__side = side self.__isDualMonitor = isDual self.__monitorWidth = monitorWidth def getProjectionDistance(self): """! Retrieve the projection distance. @return: Projection distance as an integer. """ return int(self.__projection_distance) def getProjectorDistance(self): """! Retrieve the projector distance. @return: Projector distance as an integer. """ return int(self.__projector_diatance) def getImgWidth(self): """! Retrieve the image width. @return: Image width as an integer. """ return int(self.__img_width) def getImgHeight(self): """! Retrieve the image height. @return: Image height as an integer. """ return int(self.__img_height) def getImgPath(self): """! Retrieve the image path. @return: Image path as a string. """ return str(self.__img_path) def getGamma(self): """! Retrieve the gamma value. @return: Gamma value as a float. """ return float(self.__gamma) def getOverlapWidth(self): """! Retrieve the overlap width. @return: Overlap width as an integer. """ return int(self.__overlapWidth) def getSide(self): """! Retrieve the side of projection. @return: Side as a string. """ return str(self.__side) def getIsDualMonitor(self): return bool(strtobool(self.__isDualMonitor)) def getMonitorWidth(self): return int(self.__monitorWidth) @staticmethod def readConfigFile(): """! Reads the configuration from a config.ini file and returns a Config object. @return: Config object with settings loaded from config.ini. """ import configparser config = configparser.ConfigParser() config.read('config.ini') __img_path = config["settings"]["imagePath"] __img_width = config["settings"]["imageWidth"] __img_height = config["settings"]["imageHeight"] __projection_distance = config["settings"]["projectionDistance"] __projector_diatance = config["settings"]["projectorDistance"] __gamma = config["settings"]["gamma"] __overlapWidth = config["settings"]["overlapWidth"] __side = config["settings"]["side"] __isDualMonitor = config["settings"]["isDualMonitor"] __monitorWidth = config["settings"]["monitorWidth"] return Config(__projection_distance, __projector_diatance, __img_width, __img_height, __img_path, __gamma, __overlapWidth, __side, __isDualMonitor, __monitorWidth) </code></pre> h1. display_img.py <pre><code class="python"> #!/usr/bin/env python # -*- coding: utf-8 -*- class DisplayImg: """! @brief A class to represent and manage image display properties. """ def __init__(self, w, h,overlap, img, side): """! @brief Constructor for the DisplayImg class. @param w The width of the display area. @param h The height of the display area. @param overlap The overlap percentage for the display area. @param img The image to be displayed. @param side The side or orientation of the display. """ self.__p_width = w self.__p_height = h self.__p_overlap = overlap self.__img = img self.__side = side def getImg(self): """! @brief Getter for the image. @return The image associated with the display. """ return self.__img def getWidth(self): """! @brief Getter for the width of the display. @return The width of the display area. """ return self.__p_width def getHeight(self): """! @brief Getter for the height of the display. @return The height of the display area. """ return self.__p_height def getOverlap(self): """! @brief Getter for the overlap percentage. @return The overlap percentage of the display area. """ return self.__p_overlap def getSide(self): """! @brief Getter for the side/orientation of the display. @return The side or orientation of the display. """ return self.__side </code></pre> h1. img_processor.py <pre><code class="python"> #!/usr/bin/env python # -*- coding: utf-8 -*- from config import Config from display_img import DisplayImg import numpy as np import cv2 import os class ImgProcessor: """! @brief Performs alpha blending and gamma correction. This class provides methods for image processing, including alpha blending of overlapping image regions and gamma correction for brightness adjustment. """ def __init__(self, config): """! @brief Initializes the ImgProcessor object. Sets the configuration object to retrieve parameters like overlap width and gamma values. @param config h2. config.py: Configuration object containing parameters. """ self.__config = config def alphaBlend(self, l_img, r_img): """! @brief Performs alpha blending on overlapping regions of two images. Blends the overlapping regions of the left and right images using a linear gradient alpha mask. @param l_img Left image. @param r_img Right image. @return Tuple containing processed left and right images. """ overlap_width = self.__config.getOverlapWidth() l_overlap = l_img[:, -overlap_width:, :].copy() r_overlap = r_img[:, :overlap_width, :].copy() height = l_overlap.shape[0] alpha = np.linspace(1, 0, overlap_width).reshape(1, overlap_width, 1) alpha = np.tile(alpha, (height, 1, 3)) l_overlap = (alpha * l_overlap).astype(np.uint8) r_overlap = ((1 - alpha) * r_overlap).astype(np.uint8) l_common = l_img[:, :-overlap_width, :] r_common = r_img[:, overlap_width:, :] l_processed = np.concatenate([l_common, l_overlap], axis=1) r_processed = np.concatenate([r_overlap, r_common], axis=1) return l_processed, r_processed def GammaCorrection(self, img): """! @brief Applies gamma correction to an image. Adjusts the brightness of the image using gamma correction based on the gamma value in the configuration. @param img Input image. @return Gamma-corrected image. """ gamma = self.__config.getGamma() inv_gamma = 1.0 / gamma lookup_table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in range(256)]).astype("uint8") corrected_image = cv2.LUT(img, lookup_table) return corrected_image def cropImage(self): """! @brief Crops the left and right halves of an image with overlap. Reads the image from the specified path, splits it into left and right halves with the overlap region, and returns the cropped images. @return Tuple containing cropped left and right images. """ path = str(os.getcwd() + self.__config.getImgPath()) overlap_width = self.__config.getOverlapWidth() image = cv2.imread(path) width = image.shape[1] l_img = image[:, :width // 2 + overlap_width, :] r_img = image[:, width // 2 - overlap_width:, :] return l_img, r_img </code></pre> Class