I want to convert Bitmap to YUV422 (YUYV) format. I've google about the YUYV format and tried to write this code.
path = "C:/Users/hogan/Desktop/red.bmp"
image = Image.open(path).convert("YCbCr") # YUV
image = np.asarray(image)
width, height, YUYV = image.shape[1], image.shape[0], 4
array = np.empty((height * width * 3), dtype="uint8")
Y, U, V = 0, 1, 2
count = 0
for i in range(len(image)):
for j in range(len(image[i])):
for k in range(len(image[i][j])):
if (count % 4 == 0):
array[count] = image[i][j][Y]
if (count % 4 == 1):
array[count] = image[i][j][U]
if (count % 4 == 2):
array[count] = image[i][j][Y]
if (count % 4 == 3):
array[count] = image[i][j][V]
count = count + 1
array.astype('uint8').tofile("C:/Users/hogan/Desktop/tmpdir/1.raw")
I read this image and know my code is wrong but no idea how to make it right. For Example : Red color (255,0,0) in YUV is (76,84,255), if I've a lot of pixels, I don't know which 'U' an 'V' should be dropped.
If use my code to convert a 480*640 (W*H), it will be 960*480.
You can use numpy advanced indexing and broadcasting here.
Suppose, you have an image:
ycbcr = np.array([
[['Y00', 'U00', 'V00'], ['Y01', 'U01', 'V01'], ['Y02', 'U02', 'V02']],
[['Y10', 'U10', 'V10'], ['Y11', 'U11', 'V11'], ['Y12', 'U12', 'V12']],
[['Y20', 'U20', 'V20'], ['Y21', 'U21', 'V21'], ['Y22', 'U22', 'V22']],
], dtype=str)
Working with 3D array is going to be unwieldy, so let's convert it to 1D:
ycbcr = ycbcr.reshape(27)
Then, I'll allocate an array for YUYV stream:
yuyv = np.array([' ' for _ in range(18)]) # ' ' is basically because
# I use strings in this example. You'd probably want to use arrays of uint8
First step is the easiest - we take every third value of ycbcr (Y components) and place them on even positions of yuyv:
yuyv[::2] = ycbcr[::3]
Then we proceed with other bytes. U00
should go from position 1 to position 1, V00
from 2 to 3, U01
and V01
are omitted, U02
goes from 7 to 5, V02
goes from 8 to 7, U03
is omitted and so on:
yuyv[1::4] = ycbcr[1::6] # Moving U, every sixth byte starting from position 1
yuyv[3::4] = ycbcr[2::6][:-1] # Moving V, dropping last element of V
So you get the following yuyv
, just like an image suggested:
array(['Y00', 'U00', 'Y01', 'V00', 'Y02', 'U02', 'Y10', 'V02', 'Y11',
'U11', 'Y12', 'V11', 'Y20', 'U20', 'Y21', 'V20', 'Y22', 'U22'],
dtype='<U3')
If you want to follow @alkanen advice, the approach is feasible as well, you'll just need to sample U and V bytes in two arrays and take an average. Perhaps it is going to look like this: (untested)
u_even = ycbcr[1::6]
u_odd = ycbcr[4::6]
u = (u_even + u_odd) / 2
yuyv[1::4] = u
More complex border cases will encounter as well.