Actions
Codes » History » Revision 31
« Previous |
Revision 31/33
(diff)
| Next »
Mitsuki EIKI, 01/16/2025 02:16 PM
Codes¶
Wiki | About_Us | Project_Overview | UML_Diagram | Codes
main_stitcher.py¶
#!/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()
config.py¶
#!/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)
display_img.py
#!/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
#!/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
img_processor.py
#!/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 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
#!/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 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
Updated by Mitsuki EIKI 4 months ago · 31 revisions