Сохранить кадр из TangoService_connectOnFrameAvailable

Как я могу сохранить кадр с помощью TangoService_connectOnFrameAvailable() и правильно отобразить его на своем компьютере? Как упоминается на этой справочной странице, пиксели хранятся в формате HAL_PIXEL_FORMAT_YV12. . В моей функции обратного вызова для TangoService_connectOnFrameAvailable я сохраняю фрейм следующим образом:

static void onColorFrameAvailable(void* context, TangoCameraId id, const TangoImageBuffer* buffer) 
{
  ...
  std::ofstream fp;
  fp.open(imagefile, std::ios::out | std::ios::binary );
  int offset = 0;
  for(int i = 0; i < buffer->height*2 + 1; i++) {
    fp.write((char*)(buffer->data + offset), buffer->width);
    offset += buffer->stride;
  }
  fp.close();
}

Затем, чтобы избавиться от метаданных в первой строке и отобразить изображение, которое я запускаю:

$ dd if="input.raw" of="new.raw" bs=1 skip=1280
$ vooya new.raw

Я позаботился о том, чтобы в vooya порядок каналов был yvu. Результат: yv12 test image

Что я делаю неправильно при сохранении изображения и его отображении?

ОБНОВЛЕНИЕ за ответ Марка Маллина:

int offset = buffer->stride; // header offset
// copy Y channel
for(int i = 0; i < buffer->height; i++) {
  fp.write((char*)(buffer->data + offset), buffer->width);
  offset += buffer->stride;
}
// copy V channel
for(int i = 0; i < buffer->height / 2; i++) {
  fp.write((char*)(buffer->data + offset), buffer->width / 2);
  offset += buffer->stride / 2;
}
// copy U channel
for(int i = 0; i < buffer->height / 2; i++) {
  fp.write((char*)(buffer->data + offset), buffer->width / 2);
  offset += buffer->stride / 2;
}

Теперь это показывает изображение ниже, но все еще есть некоторые артефакты; Интересно, это от камеры планшета Tango или от моей обработки необработанных данных... есть мысли?

yv12 тест 2


person t2k32316    schedule 26.01.2015    source источник


Ответы (2)


Не могу точно сказать, что вы делаете неправильно, И в изображениях танго часто есть артефакты - ваши новые, но я часто вижу нежно-голубой цвет, где блики кажутся раздражающими более глубокие системы, и когда он начинает терять синхронизацию с система глубины под нагрузкой, вы часто будете видеть то, что выглядит как блестящая сетка (я думаю, это ИК-шаблон). В конце концов, любая рациональная попытка обработать изображение с помощью openCV и т. д. не удалась, поэтому я вручную написал декодер с некоторая помощь от SO thread здесь

Тем не менее, данный буфер изображения содержит указатель на необработанные данные из Tango, а различные другие переменные, такие как рост и шаг, заполняются данными, полученными в обратном вызове, тогда эта логика создаст карту RGBA — да, я оптимизировал математику в это, так что это немного некрасиво - это более медленный, но функционально эквивалентный близнец, указанный вторым. Мой собственный опыт говорит о том, что это ужасная идея — пытаться сделать это декодирование прямо в обратном вызове (я полагаю, что Tango способен потерять синхронизацию со вспышкой для глубины по чисто злобным причинам), поэтому мой работает на этапе рендеринга.

Быстро

uchar* pData = TangoData::cameraImageBuffer;
uchar* iData = TangoData::cameraImageBufferRGBA;
int size = (int)(TangoData::imageBufferStride * TangoData::imageBufferHeight);
float invByte = 0.0039215686274509803921568627451;  // ( 1 / 255)

int halfi, uvOffset, halfj, uvOffsetHalfj;
float y_scaled, v_scaled, u_scaled;
int uOffset = size / 4 + size;
int halfstride = TangoData::imageBufferStride / 2;
for (int i = 0; i < TangoData::imageBufferHeight; ++i)
{
    halfi = i / 2;
    uvOffset = halfi * halfstride;
    for (int j = 0; j < TangoData::imageBufferWidth; ++j)
    {
        halfj = j / 2;
        uvOffsetHalfj = uvOffset + halfj;
        y_scaled = pData[i * TangoData::imageBufferStride + j] * invByte;
        v_scaled = 2 * (pData[uvOffsetHalfj + size] * invByte - 0.5f) * Vmax;
        u_scaled = 2 * (pData[uvOffsetHalfj + uOffset] * invByte - 0.5f) * Umax;
        *iData++ = (uchar)((y_scaled + 1.13983f * v_scaled) * 255.0);;
        *iData++ = (uchar)((y_scaled - 0.39465f * u_scaled - 0.58060f * v_scaled) * 255.0);
        *iData++ = (uchar)((y_scaled + 2.03211f * u_scaled) * 255.0);
        *iData++ = 255;
    }
}

понятно

for (int i = 0; i < TangoData::imageBufferHeight; ++i)
{
    for (int j = 0; j < TangoData::imageBufferWidth; ++j)
    {
        uchar y = pData[i * image->stride + j];
        uchar v = pData[(i / 2) * (TangoData::imageBufferStride / 2) + (j / 2) + size];
        uchar u = pData[(i / 2) * (TangoData::imageBufferStride / 2) + (j / 2) + size + (size / 4)];
        YUV2RGB(y, u, v);
        *iData++ = y;
        *iData++ = u;
        *iData++ = v;
        *iData++ = 255;
    }
}
person Mark Mullin    schedule 26.01.2015

Я думаю, что есть лучший способ сделать это, если вы можете сделать это в автономном режиме. Лучший способ сохранить изображение должен быть примерно таким (не забудьте создать папку «Изображения», иначе вы ничего не сохраните)

void onFrameAvailableRouter(void* context, TangoCameraId id, const TangoImageBuffer* buffer) {
  //To write the image in a txt file.
  std::stringstream name_stream;
  name_stream.setf(std::ios_base::fixed, std::ios_base::floatfield);
  name_stream.precision(3);
  name_stream << "/storage/emulated/0/Pictures/"
                <<cur_frame_timstamp_
                <<".txt";

  std::fstream f(name_stream.str().c_str(), std::ios::out | std::ios::binary);
  // size = 1280*720*1.5 to save YUV or 1280*720 to save grayscale
  int size = stride_ * height_ * 1.5;
  f.write((const char *) buffer->data,size * sizeof(uint8_t));
  f.close();
}

Затем, чтобы преобразовать файл .txt в png, вы можете сделать это

inputFolder = "input"
outputFolderRGB = "output/rgb"
outputFolderGray = "output/gray"

input_filename  = "timestamp.txt"
output_filename = "rgb.png"
allFile = listdir(inputFolder)
numberOfFile = len(allFile)

if "input" in glob.glob("*"):
    if  "output/rgb" in glob.glob("output/*"):
        print ""
    else:
        makedirs("output/rgb")
        if "output/gray" in glob.glob("output/*"):
            print ""
        else:
            makedirs("output/gray")

    #The output reportories are ready
    for file in allFile:
        count+=1
        print "current file : ",count,"/",numberOfFile
        input_filename = file
        output_filename = input_filename[0:(len(input_filename)-3)]+"png"

        # load file into buffer
        data = np.fromfile(inputFolder+"/"+input_filename, dtype=np.uint8)  

        #To get RGB image  
        # create yuv image
        yuv = np.ndarray((height + height / 2, width), dtype=np.uint8, buffer=data)    
        # create a height x width x channels matrix with the datatype uint8 for rgb image
        img = np.zeros((height, width, channels), dtype=np.uint8);    
        # convert yuv image to rgb image
        cv2.cvtColor(yuv, cv2.COLOR_YUV2BGRA_NV21, img, channels)
        cv2.imwrite(outputFolderRGB+"/"+output_filename, img)

        #If u saved the image in graysacale use this part instead
        #yuvReal = np.ndarray((height, width), dtype=np.uint8, buffer=data)
        #cv2.imwrite(outputFolderGray+"/"+output_filename, yuvReal)
else:
    print "not any input"

Вам просто нужно поместить свой .txt в папку input. Это скрипт на Python, но если вы предпочитаете версию на C++, это очень близко.

person Bastienm    schedule 10.08.2016