void ColorTransform::ProcessFrame(FrameSetPtr input, list<FrameSetPtr>* output) {
    VideoFrame* frame = dynamic_cast<VideoFrame*>(input->at(video_stream_idx_).get());
    ASSERT_LOG(frame);
    
    IplImage image;
    frame->ImageView(&image);

    // Read transform weights.
    float weights[3];
    float shift;
    ifs_ >> weights[0] >> weights[1] >> weights[2] >> shift;
    
    for (int i = 0; i < frame_height_; ++i) {
      uchar* color_ptr = RowPtr<uchar>(&image, i);
      
      for (int j = 0; j < frame_width_; ++j, color_ptr +=3 ) {
        color_ptr[0] = (uchar) clamp_uchar((float)color_ptr[0] * weights[0] * shift);
        color_ptr[1] = (uchar) clamp_uchar((float)color_ptr[1] * weights[1] * shift);
        color_ptr[2] = (uchar) clamp_uchar((float)color_ptr[2] * weights[2] * shift);
      }
    }
    
    output->push_back(input);
  }
  void ColorCheckerUnit::ProcessFrame(FrameSetPtr input, list<FrameSetPtr>* output) {
    VideoFrame* frame = dynamic_cast<VideoFrame*>(input->at(video_stream_idx_).get());
    ASSERT_LOG(frame);

    IplImage image;
    frame->ImageView(&image);
    
    const VideoFrame* lum_frame = dynamic_cast<const VideoFrame*>(input->at(lum_stream_idx_).get());
    ASSERT_LOG(lum_frame);
    
    IplImage lum_image;
    lum_frame->ImageView(&lum_image);
    
    // Sum according to checker image.
    vector<CvPoint3D32f> colors(256);
    vector<float> pixel_num(256);
    
    for (int i = 0; i < frame_height_; ++i) {
      const uchar* mask_ptr = RowPtr<const uchar>(checker_img_, i);
      uchar* color_ptr = RowPtr<uchar>(&image, i);
      const uchar* lum_ptr = RowPtr<const uchar>(&lum_image, i);
      
      for (int j = 0; j < frame_width_; ++j, ++mask_ptr, color_ptr +=3, ++lum_ptr) {
        int idx = mask_ptr[0];
        if (idx == 0)     // Don't sample background
          continue;
        
        // Is pixel within range?
        int min_val = std::min(std::min(color_ptr[0], color_ptr[1]), color_ptr[2]);
        int max_val = std::max(std::max(color_ptr[0], color_ptr[1]), color_ptr[2]);
        
        if (min_val >= min_thresh_ && max_val <= max_thresh_) {
//        if (lum_ptr[0] >= min_thresh_ && lum_ptr[0] <= max_thresh_) {
          ++pixel_num[idx];
          colors[idx] = colors[idx] + cvPoint3D32f(color_ptr[0],
                                                   color_ptr[1],
                                                   color_ptr[2]);
          // Mark pixel as processed!
          color_ptr[0] ^= 0xff;
          color_ptr[1] ^= 0xff;
          color_ptr[2] ^= 0xff;
        }
      }
    }
    
    if (frame_num_ != 0)
      ofs_ << "\n";
    
    // Normalize and output.
    int processed_colors = 0;
    for (vector<int>::const_iterator c = checker_ids_.begin(); c != checker_ids_.end(); ++c) {
      if (pixel_num[*c] > 0) {
        ++processed_colors;
        colors[*c] = colors[*c] * (1.0 / pixel_num[*c]);
      }
      
      ofs_ << *c << " " << pixel_num[*c] << " " << colors[*c].x << " " 
           << colors[*c].y << " " << colors[*c].z << " ";
    }
    std::cout << "Frame " << frame_num_ << ": Processed colors " << processed_colors << "\n";
    
    ++frame_num_;
    output->push_back(input);
  }