Ejemplo n.º 1
0
def get_image_from_texture2d(texture_2d, flip=True) -> Image:
    """converts the given texture into PIL.Image

    :param texture_2d: texture to be converterd
    :type texture_2d: Texture2D
    :param flip: flips the image back to the original (all Unity textures are flipped by default)
    :type flip: bool
    :return: PIL.Image object
    :rtype: Image
    """
    image_data = copy(bytes(texture_2d.image_data))
    if not image_data:
        return Image.new("RGB", (0, 0))

    texture_format = (
        texture_2d.m_TextureFormat
        if isinstance(texture_2d.m_TextureFormat, TF)
        else TF(texture_2d.m_TextureFormat)
    )
    selection = CONV_TABLE[texture_format]

    if len(selection) == 0:
        raise NotImplementedError(
            f"Not implemented texture format: {texture_format.name}"
        )

    if texture_format in XBOX_SWAP_FORMATS:
        image_data = swap_bytes_for_xbox(image_data, texture_2d.platform)

    if "Crunched" in texture_format.name:
        version = texture_2d.version
        if (
            version[0] > 2017
            or (version[0] == 2017 and version[1] >= 3)  # 2017.3 and up
            or texture_format == TF.ETC_RGB4Crunched
            or texture_format == TF.ETC2_RGBA8Crunched
        ):
            image_data = texture2ddecoder.unpack_unity_crunch(image_data)
        else:
            image_data = texture2ddecoder.unpack_crunch(image_data)

    img = selection[0](
        image_data, texture_2d.m_Width, texture_2d.m_Height, *selection[1:]
    )

    if img and flip:
        return img.transpose(Image.FLIP_TOP_BOTTOM)
    return img
Ejemplo n.º 2
0
def _test_crunched(name, func, unity=False):
    # load sample data
    data: bytes = zip.open(name + ".data", "r").read()
    details: dict = json.loads(zip.open(name + ".json", "r").read())
    ori_img: Image = Image.open(zip.open(name + ".png", "r"))

    if unity:
        data = texture2ddecoder.unpack_unity_crunch(data)
    else:
        data = texture2ddecoder.unpack_crunch(data)

    # decompress data
    width = details["m_Width"]
    height = details["m_Height"]
    dec = func(data, width, height)

    # load raw image data
    dec_img = Image.frombytes("RGBA", (width, height), dec, 'raw', ("BGRA"))
    dec_img = dec_img.convert(ori_img.mode)
    # compare images
    assert (ImageChops.difference(ori_img, dec_img).getbbox() is None)
Ejemplo n.º 3
0
 def get_resource(self, path_id: int):
     obj = self.asset.objects[path_id]
     if obj.type == 'Texture2D':
         data = obj.read()
         if len(data.data) == 0:
             logger.warning("Data doesn't exist")
             return Resource()
         # logger.warning(f"{self.container[path_id]} is {data.format.name}")
         if data.format.name == 'ETC_RGB4':
             # 이미지 포맷: ETC1 -> RGB(A)
             # Alpha 채널도 나오긴 하는데 의미 없어서 자름
             decoded = texture2ddecoder.decode_etc1(data.data, data.width,
                                                    data.height)
             im = np.frombuffer(decoded,
                                np.uint8).reshape(data.height, data.width,
                                                  4)
             im = cv2.flip(im, 0)
             im_a = self.get_alpha_image(path_id)
             # if we have alpha, try to merge
             if im_a is not None:
                 # 알파 채널 이미지가 원래 이미지와 크기가 다른 경우가 있는 경우를 위한 리사이징
                 if im_a.shape[:2] != (data.height, data.width):
                     im_a = cv2.resize(im_a,
                                       None,
                                       fx=data.height / im_a.shape[0],
                                       fy=data.width / im_a.shape[1])
                 im[:, :, 3] = im_a
                 return ResImage(im, data.name)
             else:
                 return ResImage(cv2.cvtColor(im, cv2.COLOR_BGRA2BGR),
                                 data.name)
         elif data.format.name == 'ETC_RGB4Crunched':
             decrunched = texture2ddecoder.unpack_unity_crunch(data.data)
             # 이미지 포맷: ETC1 -> RGB(A)
             # Alpha 채널도 나오긴 하는데 의미 없어서 자름
             decoded = texture2ddecoder.decode_etc1(decrunched, data.width,
                                                    data.height)
             im = np.frombuffer(decoded,
                                np.uint8).reshape(data.height, data.width,
                                                  4)
             im = cv2.flip(im, 0)
             # attempt to find alpha image
             im_a = self.get_alpha_image(path_id)
             # if we have alpha, try to merge
             if im_a is not None:
                 # 알파 채널 이미지가 원래 이미지와 크기가 다른 경우가 있는 경우를 위한 리사이징
                 if im_a.shape[:2] != (data.height, data.width):
                     im_a = cv2.resize(im_a,
                                       None,
                                       fx=data.height / im_a.shape[0],
                                       fy=data.width / im_a.shape[1])
                 im[:, :, 3] = im_a
                 return ResImage(im, data.name)
             else:
                 return ResImage(cv2.cvtColor(im, cv2.COLOR_BGRA2BGR),
                                 data.name)
         elif data.format.name == "ETC2_RGBA8":
             decoded = texture2ddecoder.decode_etc2a8(
                 data.data, data.width, data.height)
             im = np.frombuffer(decoded,
                                np.uint8).reshape(data.height, data.width,
                                                  4)
             return ResImage(cv2.flip(im, 0), data.name)
         elif data.format.name == 'ETC2_RGBA8Crunched':
             decrunched = texture2ddecoder.unpack_unity_crunch(data.data)
             decoded = texture2ddecoder.decode_etc2a8(
                 decrunched, data.width, data.height)
             im = np.frombuffer(decoded,
                                np.uint8).reshape(data.height, data.width,
                                                  4)
             return ResImage(cv2.flip(im, 0), data.name)
         elif data.format.name == 'RGBA32':
             data_size = data.height * data.width * 4
             # 이미지 포맷: RGBA32 -> RGBA
             im = np.frombuffer(data.data[:data_size],
                                dtype=np.uint8).reshape(
                                    data.height, data.width, 4)
             # cv2에서는 BGRA 색역을 쓰기 때문에 변한
             im = cv2.cvtColor(im, cv2.COLOR_RGBA2BGRA)
             # 소전은 뒤집힌거 쓰니까 이미지 뒤집기
             im = cv2.flip(im, 0)
             return ResImage(im, data.name)
         elif data.format.name == 'RGBA4444':
             shape = (data.height, data.width)
             data_size = data.height * data.width * 2
             # numpy array로 변환 (아직 1차원 배열)
             im_array = np.frombuffer(data.data[:data_size], dtype=np.uint8)
             # 비트 시프트 + 값 곱하기 후 3차원 배열로 만든 후 채널별로 분리
             # 0x0 -> 0x00, 0x1 -> 0x11, ... , 0xF -> 0xFF
             im_b, im_r = cv2.split(
                 (np.bitwise_and(im_array >> 4, 0x0f) * 17).reshape(
                     *shape, 2))
             im_a, im_g = cv2.split(
                 (np.bitwise_and(im_array, 0x0f) * 17).reshape(*shape, 2))
             return ResImage(
                 cv2.flip(cv2.merge((im_b, im_g, im_r, im_a)), 0),
                 data.name)
         elif data.format.name == 'ARGB4444':
             shape = (data.height, data.width)
             data_size = data.height * data.width * 2
             # numpy array로 변환 (아직 1차원 배열)
             im_array = np.frombuffer(data.data[:data_size], dtype=np.uint8)
             # 비트 시프트 + 값 곱하기 후 3차원 배열로 만든 후 채널별로 분리
             # 0x0 -> 0x00, 0x1 -> 0x11, ... , 0xF -> 0xFF
             im_g, im_a = cv2.split(
                 (np.bitwise_and(im_array >> 4, 0x0f) * 17).reshape(
                     *shape, 2))
             im_b, im_r = cv2.split(
                 (np.bitwise_and(im_array, 0x0f) * 17).reshape(*shape, 2))
             return ResImage(
                 cv2.flip(cv2.merge((im_b, im_g, im_r, im_a)), 0),
                 data.name)
         elif data.format.name == 'ARGB32':
             # 이미지 포맷: ARGB32 -> RGBA
             # 채널별로 분리 후 다시 합침
             # 이상하게 원래 크기 이상으로 뭔가 데이터가 있는데 딱히 필요는 없어서 모양으로 계산해서 필요한 부분만 슬라이싱
             data_size = data.height * data.width * 4
             im_array = np.frombuffer(data.data[:data_size],
                                      dtype=np.uint8).reshape(
                                          data.height, data.width, 4)
             im_array = cv2.cvtColor(cv2.flip(im_array, 0),
                                     cv2.COLOR_RGBA2BGRA)
             return ResImage(im_array, data.name)
         elif data.format.name == 'Alpha8':
             # 알파 이미지
             shape = (data.height, data.width)
             return ResImage(
                 cv2.flip(
                     np.frombuffer(data.data,
                                   dtype=np.uint8).reshape(shape), 0),
                 data.name)
         elif data.format.name == 'RGB24':
             # 이미지 포맷: RGB24 -> RGB
             # 간단하게 처리
             data_size = data.height * data.width * 3
             im_rgb = np.frombuffer(data.data[:data_size],
                                    dtype=np.uint8).reshape(
                                        data.height, data.width, 3)
             # attempt to find alpha image
             im_a = self.get_alpha_image(path_id)
             if im_a is not None:
                 # 알파 채널 이미지가 원래 이미지와 크기가 다른 경우가 있는 경우를 위한 리사이징
                 if im_a.shape[:2] != (data.height, data.width):
                     im_a = cv2.resize(im_a,
                                       None,
                                       fx=data.height / im_a.shape[0],
                                       fy=data.width / im_a.shape[1])
                 im_a = cv2.flip(
                     im_a, 0
                 )  # will end up flipping alpha twice below.. is there a better way?
                 im_bgra = cv2.cvtColor(im_rgb, cv2.COLOR_RGB2BGRA)
                 try:
                     im_bgra[:, :, 3] = im_a
                 except ValueError:
                     # skip alpha if we can't merge it back, for some odd reason this happens on certain files
                     # (cafe chair texture.. etc) as of Dec 22, 2018 on both TW, and CN beta version
                     logger.warning(
                         "dimension mismatch between converted rgb/alpha data"
                     )
                 return ResImage(cv2.flip(im_bgra, 0), data.name)
             else:
                 return ResImage(
                     cv2.flip(cv2.cvtColor(im_rgb, cv2.COLOR_RGB2BGR), 0),
                     data.name)
         elif data.format.name == 'RGB565':
             im_array = np.frombuffer(data.data, dtype=np.uint8).reshape(
                 data.height, data.width, 2)
             return ResImage(
                 cv2.flip(cv2.cvtColor(im_array, cv2.COLOR_BGR5652BGR), 0),
                 data.name)
         elif data.format.name == 'PVRTC_RGBA4' or data.format.name == 'PVRTC_RGBA2':
             decoded = texture2ddecoder.decode_pvrtc(
                 data.data, data.width, data.height)
             im = np.frombuffer(decoded,
                                np.uint8).reshape(data.height, data.width,
                                                  4)
             return ResImage(cv2.flip(im, 0), data.name)
         else:
             # 모르는 포맷은 그냥 건너뜀
             raise Exception('Unexpected texture format ' +
                             str(data.format))
             return Resource(data.name)
     elif obj.type == 'TextAsset':
         data = obj.read()
         # is there a better way?
         if "/live2d/" in self.container.get(path_id):
             return ResL2D(data.bytes, data.name)
         elif isinstance(data.script, str):
             # 데이터가 일반 텍스트(txt)인경우 str 로 리턴
             return ResText(data.script, data.name)
         else:
             # 아니면 Bytes 형태로 리턴
             return ResBytes(data.bytes, data.name)
     else:
         return Resource()