title: Python OpenCV converting planar YUV 4:2:0 image to RGB -- YUV array format
body: <p>I am trying to use OpenCV, version 4.1.0 through python to convert a planar YUV 4:2:0 image to RGB and am struggling to understand how to format the array to pass to the <code>cvtColor</code> function. I have all 3 channels as separate arrays and am trying to merge them for use with <code>cv2.cvtColor</code>. I am using <code>cv2.cvtColor(yuv_array, cv2.COLOR_YUV420p2RGB)</code>. I understand that the <code>yuv_array</code> should be 1.5x as tall as the original image (that's what a yuv array from <code>cvtColor</code> using <code>cv2.COLOR_RGB2YUV_YV12</code> looks like) and I should put the UV components into the bottom half of the <code>yuv_array</code> and the Y channel into the top part of the array.</p>
  
  <p>I cannot seem to figure out how the U and V channels should be formatted within the bottom of this array. I've tried interleaving them and just putting them both in there back-to-back. With both methods, I've tried putting U first then V and also the other way around. All methods lead to artifacts in the resulting image. Here is my code and an example image:</p>
  
  <pre><code>import os
  import errno
  import numpy as np
  import cv2
  
  fifo_names = ["/tmp/fifos/y_fifo", "/tmp/fifos/u_fifo", "/tmp/fifos/v_fifo"]
  
  #teardown; delete fifos
  import signal, sys
  def cleanup_exit(signal, frame):
      print ("cleaning up!")
      for fifo in fifo_names:
          os.remove(fifo)
      sys.exit(0)
  signal.signal(signal.SIGINT, cleanup_exit)
  signal.signal(signal.SIGTERM, cleanup_exit)
  
  #make fifos
  for fifo in fifo_names:
      try:
          os.mkfifo(fifo);
      except OSError as oe:
          if oe.errno == errno.EEXIST:
              os.remove(fifo)
              os.mkfifo(fifo)
          else:
              raise()
  
  #make individual np arrays to store Y,U, and V channels
  #we know the image size beforehand -- 640x360 pixels
  yuv_data = []
  frame_size = []
  fullsize = (360, 640)
  halfsize = (180, 320)
  for i in range(len(fifo_names)):
      if (i == 0):
          size = fullsize
      else:
          size = halfsize
      yuv_data.append(np.empty(size, dtype=np.uint8));
      frame_size.append(size)
  
  #make array that holds all yuv data for display with cv2
  all_yuv_data = np.empty((fullsize[0] + halfsize[0], fullsize[1]), dtype=np.uint8) 
  
  #continuously read yuv images from fifos
  print("waiting for fifo to be written to...")
  while True:
      for i in range(len(fifo_names)):
          fifo = fifo_names[i]
          with open(fifo, 'rb') as f:
              print("FIFO %s opened" % (fifo))
              all_data = b''
              while True:
                  data = f.read()
                  print("read from %s, len: %d" % (fifo,len(data)))
                  if len(data) == 0: #then the fifo has been closed
                      break
                  else:
                      all_data += data
              yuv_data[i] = np.frombuffer(all_data, dtype=np.uint8).reshape(frame_size[i])
  
      #stick all yuv data in one buffer, interleaving columns
      all_yuv_data[0:fullsize[0],0:fullsize[1]] = yuv_data[0]
      all_yuv_data[fullsize[0]:,0:fullsize[1]:2] = yuv_data[1]
      all_yuv_data[fullsize[0]:,1:fullsize[1]:2] = yuv_data[2]
  
      #show each yuv channel individually
      cv2.imshow('y', yuv_data[0])
      cv2.imshow('u', yuv_data[1])
      cv2.imshow('v', yuv_data[2])
  
      #convert yuv to rgb and display it
      rgb = cv2.cvtColor(all_yuv_data, cv2.COLOR_YUV420p2RGB);
      cv2.imshow('rgb', rgb)
      cv2.waitKey(1)
  </code></pre>
  
  <p>The above code is trying to interleave the U and V information column-wise.</p>
  
  <p>I have also tried using the following to place the U and V channel information into the <code>all_yuv_data</code> array:</p>
  
  <pre><code>    #try back-to-back
      all_yuv_data[0:fullsize[0],0:fullsize[1]] = yuv_data[0]
      all_yuv_data[fullsize[0]:,0:halfsize[1]] = yuv_data[1]
      all_yuv_data[fullsize[0]:,halfsize[1]:] = yuv_data[2]
  </code></pre>
  
  <p>The image is a frame of video obtained with libav from another program. The frame is of format <code>AV_PIX_FMT_YUV420P</code>, <a href="https://libav.org/documentation/doxygen/master/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5" rel="nofollow noreferrer">described as</a> "planar YUV 4:2:0, 12bpp, (1 Cr &amp; Cb sample per 2x2 Y samples)".</p>
  
  <p>Here are the yuv channels for a sample image shown in grayscale:</p>
  
  <p><strong>Y Channel:</strong></p>
  
  <p><img src="https://i.stack.imgur.com/O9Ege.png" alt="y channel"></p>
  
  <p><strong>U Channel:</strong></p>
  
  <p><img src="https://i.stack.imgur.com/WhKjD.png" alt="u channel"> </p>
  
  <p><strong>V Channel:</strong></p>
  
  <p><img src="https://i.stack.imgur.com/3ROiU.png" alt="v channel"></p>
  
  <p>and the corresponding RGB conversion (this was from using the above interleaving method, similar artifacts are seen when using the 'back-to-back' method):</p>
  
  <p><strong>RGB Image With Artifacts:</strong></p>
  
  <p><img src="https://i.stack.imgur.com/ilFr2.png" alt="rgb image with artifacts"></p>
  
  <p>How should I be placing the u and v channel information in <code>all_yuv_data</code>?</p>
  
  <p><strong>Edited by Mark Setchell after this point</strong></p>
  
  <p>I believe the expected result is:</p>
  
  <p><a href="https://i.stack.imgur.com/ff99O.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ff99O.jpg" alt="enter image description here"></a></p>