Beispiel #1
0
class VLImage:
    """
    Class image.

    Attributes:
        coreImage (CoreFE.Image): core image object
        source (Union[bytes, bytearray, PilImage, CoreImage]): body of image
        filename (str): filename of the file which is source of image
    """

    __slots__ = ("coreImage", "source", "filename")

    def __init__(
        self,
        body: Union[bytes, bytearray, PilImage, CoreImage],
        colorFormat: Optional[ColorFormat] = None,
        filename: str = "",
    ):
        """
        Init.

        Args:
            body: body of image - bytes numpy array or core image
            colorFormat: img format to cast into
            filename: user mark a source of image
        Raises:
            TypeError: if body has incorrect type
            LunaSDKException: if failed to load image to sdk Image
        """
        if isinstance(body, bytearray):
            body = bytes(body)

        if isinstance(body, CoreImage):
            if colorFormat is None or colorFormat.coreFormat == body.getFormat():
                self.coreImage = body
            else:
                error, self.coreImage = body.convert(colorFormat.coreFormat)
                assertError(error)

        elif isinstance(body, bytes):
            self.coreImage = CoreImage()
            imgFormat = (colorFormat or ColorFormat.R8G8B8).coreFormat
            error = self.coreImage.loadFromMemory(body, len(body), imgFormat)
            assertError(error)

        elif isinstance(body, np.ndarray):
            mode = getNPImageType(body)
            self.coreImage = self._coreImageFromNumpyArray(
                ndarray=body, inputColorFormat=ColorFormat.load(mode), colorFormat=colorFormat or ColorFormat.R8G8B8
            )
        elif isinstance(body, PilImage):
            # prevent palette-mode colorful images conversion to grayscale image by converting it
            # with considering palette info
            # modes description: https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes
            blackNWhiteModes = ("1", "L", "LA", "La")
            if body.mode == "P" and body.palette is not None and body.palette.mode not in blackNWhiteModes:
                body = body.convert()
            array = pilToNumpy(body)
            inputColorFormat = ColorFormat.load(body.mode)
            self.coreImage = self._coreImageFromNumpyArray(
                ndarray=array, inputColorFormat=inputColorFormat, colorFormat=colorFormat or ColorFormat.R8G8B8
            )
        else:
            raise TypeError(f"Bad image type: {type(body)}")

        self.source = body
        self.filename = filename

    @classmethod
    def rotate(cls, image: "VLImage", angle: RotationAngle):
        """
        Get rotated copy of VLImage
        Args:
            image: vl image
            angle: angle for rotation
        Warnings:
            remove after FSDK-2809
        Returns:
            rotated vl image
        """
        if angle == RotationAngle.ANGLE_90:
            angleForRotation = pilImage.ROTATE_90
        elif angle == RotationAngle.ANGLE_270:
            angleForRotation = pilImage.ROTATE_270
        elif angle == RotationAngle.ANGLE_180:
            angleForRotation = pilImage.ROTATE_180
        else:
            return copy(image)

        newPilImage = image.asPillow().transpose(angleForRotation)

        return cls(newPilImage, filename=image.filename)

    @classmethod
    def load(
        cls, *, filename: Optional[str] = None, url: Optional[str] = None, colorFormat: Optional[ColorFormat] = None
    ) -> "VLImage":

        """
        Load image from numpy array or file or url.

        Args:
            *: for positional argument removal
            filename: filename
            url: url
            colorFormat: img format to cast into

        Returns:
            vl image
        Raises:
            ValueError: if no one argument  did not set.

        >>> VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg').rect
        x = 0, y = 0, width = 1000, height = 1288

        todo: more doc test
        """
        if filename is not None:
            path = Path(filename)
            with path.open("rb") as file:
                body = file.read()
                img = cls(body, colorFormat)
                img.filename = path.name
                return img

        if url is not None:
            response = requests.get(url=url)
            if response.status_code == 200:
                img = cls(response.content, colorFormat)
                img.filename = url
                return img
        raise ValueError

    @staticmethod
    def _coreImageFromNumpyArray(
        ndarray: np.ndarray, inputColorFormat: ColorFormat, colorFormat: Optional[ColorFormat] = None
    ) -> CoreImage:
        """
        Load VLImage from numpy array into `self`.

        Args:
            ndarray: numpy pixel array
            inputColorFormat: numpy pixel array format
            colorFormat: pixel format to cast into

        Returns:
            core image instance
        """
        baseCoreImage = CoreImage()
        baseCoreImage.setData(ndarray, inputColorFormat.coreFormat)
        if colorFormat is None or baseCoreImage.getFormat() == colorFormat.coreFormat:
            return baseCoreImage

        error, convertedCoreImage = baseCoreImage.convert(colorFormat.coreFormat)
        assertError(error)

        return convertedCoreImage

    @classmethod
    def fromNumpyArray(
        cls,
        arr: np.ndarray,
        inputColorFormat: Optional[Union[str, ColorFormat]] = None,
        colorFormat: Optional[ColorFormat] = None,
        filename: str = "",
    ) -> "VLImage":
        """
        Load VLImage from numpy array.

        Args:
            arr: numpy pixel array
            inputColorFormat: input numpy pixel array format
            colorFormat: pixel format to cast into
            filename: optional filename

        Returns:
            vl image
        """
        if isinstance(inputColorFormat, str):
            inputColorFormat = ColorFormat.load(inputColorFormat)

        if inputColorFormat is None:
            imageType = getNPImageType(arr)
            inputColorFormat = ColorFormat.load(imageType)

        coreImage = cls._coreImageFromNumpyArray(
            ndarray=arr, inputColorFormat=inputColorFormat, colorFormat=colorFormat
        )
        img = cls(coreImage, filename=filename)
        img.source = arr
        return img

    @property
    def format(self) -> ColorFormat:
        """
        Get image format.

        >>> image = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> image.format.value
        'R8G8B8'
        """
        return ColorFormat.convertCoreFormat(self.coreImage.getFormat())

    @property
    def rect(self) -> Rect:
        """
        Get rect of image.

        Returns:
            rect of the image
        """
        return Rect.fromCoreRect(self.coreImage.getRect())

    def computePitch(self, rowWidth) -> int:
        """
        Compute row size in bytes

        Args:
            rowWidth: row width in pixels.

        Returns:
            row size in bytes.
        """
        return self.coreImage.computePitch(rowWidth)

    @property
    def bitDepth(self) -> int:
        """
        Get number of bits per pixel.

        Returns:
            number of bits per pixel.
        """
        return self.coreImage.getBitDepth()

    @property
    def getByteDepth(self) -> int:
        """
        Get number of bytes per pixel.

        Returns:
            number of bytes per pixel.
        """
        return self.coreImage.getByteDepth()

    @property
    def channelCount(self) -> int:
        """
        Get chanel count of the image.

        Returns:
            channel count.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelCount
        3
        """
        return self.coreImage.getChannelCount()

    @property
    def channelSize(self) -> int:
        """
        Get size of one chanel in bites.

        Returns:
            channel size in bytes.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelSize
        8
        """
        return self.coreImage.getChannelSize()

    @property
    def channelStep(self) -> int:
        """
        Get chanel step.
        todo: more description

        Returns:
            channel size in bytes.
        Notes:
            padding bytes are considered spare channels.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelStep
        3
        """
        return self.coreImage.getChannelStep()

    def asNPArray(self) -> np.ndarray:
        """
        Get image as numpy array.

        !!!WARNING!!! Does NOT return the same image as in the self.coreImage.

        Returns:
            numpy array
        todo: doctest
        """
        if self.format == ColorFormat.R16:
            return self.coreImage.getDataR16()
        return self.coreImage.getData()

    def asPillow(self) -> PilImage:
        """
        Get image as pillow image.

        !!!WARNING!!! Does NOT return the same image as in the self.coreImage.

        Returns:
            pillow image
        todo: doctest
        """
        imageArray = self.asNPArray()
        return pilImage.fromarray(imageArray)

    def isBGR(self) -> bool:
        """
        Check whether format image is bgr or not.

        Returns:
            True if the image is bgr image otherwise False
        Notes:
            padding is ignored for padded channels.

        >>> VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg').isBGR()
        False
        """
        return self.coreImage.isBGR()

    def isPadded(self) -> bool:
        """
        Determinate image format has padding bytes or not.

        Returns:
            true if image format has padding bytes.
        todo examples
        """
        return self.coreImage.isPadded()

    def save(self, filename: str, colorFormat: Optional[ColorFormat] = None):
        """
        Save image to disk. Support image format: *ppm, jpg, png, tif*.

        Args:
            filename: filename
            colorFormat: color format to save image in
        Raises:
            LunaSDKException: if failed to save image to sdk Image
        """
        if colorFormat is None:
            error = self.coreImage.save(filename)
        else:
            error = self.coreImage.save(filename, colorFormat.coreFormat)
        assertError(error)

    def convertToBinaryImg(self, imageFormat: ImageFormat = ImageFormat.PPM) -> bytes:
        """
        Convert VL image to binary image
        Args:
            imageFormat: format
        Returns:
            bytes
        """
        pass

    def isValid(self) -> bool:
        """
        Check image is valid loaded  to core image or not

        Returns:
            True if image is valid otherwise False
        """
        return self.coreImage.isValid()

    def convert(self, colorFormat: ColorFormat) -> "VLImage":
        """
        Convert current VLImage into image with another color format.

        Args:
            colorFormat: color format to convert into

        Returns:
            converted vl image

        Raises:
            LunaSDKException: if failed to convert image
        """
        error, coreImage = self.coreImage.convert(colorFormat.coreFormat)
        assertError(error)

        return self.__class__(body=coreImage, filename=self.filename)
Beispiel #2
0
class VLImage:
    """
    Class image.

    Attributes:
        coreImage (CoreFE.Image): core image object
        source (str): source of image (todo change)
        filename (str): filename of the file which is source of image
    """

    __slots__ = ("coreImage", "source", "filename")

    def __init__(
        self,
        body: Union[bytes, bytearray, ndarray, CoreImage],
        imgFormat: Optional[ColorFormat] = None,
        filename: str = "",
    ):
        """
        Init.

        Args:
            body: body of image - bytes numpy array or core image
            imgFormat: img format
            filename: user mark a source of image
        Raises:
            TypeError: if body has incorrect type
            LunaSDKException: if failed to load image to sdk Image
        """
        if imgFormat is None:
            imgFormat = ColorFormat.R8G8B8
        self.coreImage = CoreImage()

        if isinstance(body, CoreImage):
            self.coreImage = body
        elif isinstance(body, bytes):
            error = self.coreImage.loadFromMemory(body, len(body),
                                                  imgFormat.coreFormat)
            if error.isError:
                raise LunaSDKException(LunaVLError.fromSDKError(error))
        elif isinstance(body, bytearray):
            error = self.coreImage.loadFromMemory(bytes(body), len(body),
                                                  imgFormat.coreFormat)
            if error.isError:
                raise LunaSDKException(LunaVLError.fromSDKError(error))
        elif isinstance(body, ndarray):
            #: todo, format ?????
            self.coreImage.setData(body, imgFormat.coreFormat)
        else:
            raise TypeError("Bad image type")

        self.source = body
        self.filename = filename

    @classmethod
    def load(cls,
             *_,
             filename: Optional[str] = None,
             url: Optional[str] = None,
             imgFormat: Optional[ColorFormat] = None) -> "VLImage":
        """
        Load imag from numpy array or file or url.

        Args:
            *_: for remove positional argument
            filename: filename
            url: url
            imgFormat:

        Returns:
            vl image
        Raises:
            ValueError: if no one argument  did not set.

        >>> VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg').rect
        x = 0, y = 0, width = 1000, height = 1288

        todo: more doc test
        """
        if filename is not None:
            path = Path(filename)
            with path.open("rb") as file:
                body = file.read()
                img = cls(body, imgFormat)
                img.source = path.name
                return img

        if url is not None:
            response = requests.get(url=url)
            if response.status_code == 200:
                img = cls(response.content, imgFormat)
                img.source = url
                return img
        raise ValueError

    @property
    def format(self) -> ColorFormat:
        """ getFormat(self: FaceEngine.Image) -> FaceEngine.FormatType

        >>> image = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> image.format.value
        'R8G8B8'
        """
        return ColorFormat.convertCoreFormat(self.coreImage.getFormat())

    @property
    def rect(self) -> Rect:
        """
        Get rect of image.

        Returns:
            rect of the image
        """
        return Rect.fromCoreRect(self.coreImage.getRect())

    def computePitch(self, rowWidth) -> int:
        """
        Compute row size in bytes

        Args:
            rowWidth: row width in pixels.

        Returns:
            row size in bytes.
        """
        return self.coreImage.computePitch(rowWidth)

    @property
    def bitDepth(self) -> int:
        """
        Get number of bits per pixel.

        Returns:
            number of bits per pixel.
        """
        return self.coreImage.getBitDepth()

    @property
    def getByteDepth(self) -> int:
        """
        Get number of bytes per pixel.

        Returns:
            number of bytes per pixel.
        """
        return self.coreImage.getByteDepth()

    @property
    def channelCount(self) -> int:
        """
        Get chanel count of the image.

        Returns:
            channel count.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelCount
        3
        """
        return self.coreImage.getChannelCount()

    @property
    def channelSize(self) -> int:
        """
        Get size of one chanel in bites.

        Returns:
            channel size in bytes.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelSize
        8
        """
        return self.coreImage.getChannelSize()

    @property
    def channelStep(self) -> int:
        """
        Get chanel step.
        todo: more description

        Returns:
            channel size in bytes.
        Notes:
            padding bytes are considered spare channels.

        >>> img = VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg')
        >>> img.channelStep
        3
        """
        return self.coreImage.getChannelStep()

    def asNPArray(self) -> ndarray:
        """
        Get image as numpy array.

        Returns:
            numpy array
        todo: doctest
        """
        return self.coreImage.getData()

    def isBGR(self) -> bool:
        """
        Check whether format image is bgr or not.

        Returns:
            True if the image is bgr image otherwise False
        Notes:
            padding is ignored for padded channels.

        >>> VLImage.load(url='https://st.kp.yandex.net/im/kadr/3/1/4/kinopoisk.ru-Keira-Knightley-3142930.jpg').isBGR()
        False
        """
        return self.coreImage.isBGR()

    def isPadded(self) -> bool:
        """
        Determinate image format has padding bytes or not.

        Returns:
            true if image format has padding bytes.
        todo examples
        """
        return self.coreImage.isPadded()

    def save(self, filename: str):
        """
        Save image to disk. Support image format: *ppm, jpg, png, tif*.

        Args:
            filename: filename
        Raises:
            todo it
        """
        saveRes = self.coreImage.save(filename)
        if saveRes.isError:
            raise ValueError

    def convertToBinaryImg(self,
                           imageFormat: ImageFormat = ImageFormat.PPM
                           ) -> bytes:
        """
        Convert VL image to binary image
        Args:
            imageFormat: format
        Returns:
            bytes
        """
        pass

    def isValid(self) -> bool:
        """
        Check image is valid loaded  to core image or not

        Returns:
            True if image is valid otherwise False
        """
        return self.coreImage.isValid()