OpenCVImageStabilizer.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package VisionX::ArmarXObjects::OpenCVImageStabilizer
17  * @author David Sippel ( uddoe at student dot kit dot edu )
18  * @date 2017
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #include "OpenCVImageStabilizer.h"
24 
26 
27 #define SMOOTHING_RADIUS 3
28 
29 using namespace armarx;
30 
31 void
33 {
34  providerName = getProperty<std::string>("providerName").getValue();
35  usingImageProvider(providerName);
36 
37  frameRate = getProperty<float>("Framerate").getValue();
38  drawFeatures = getProperty<bool>("drawFeatures").getValue();
39 }
40 
41 void
43 {
44  std::unique_lock lock(imageMutex);
45 
46  visionx::ImageProviderInfo imageProviderInfo = getImageProvider(providerName);
47  imageProviderPrx = getProxy<visionx::ImageProviderInterfacePrx>(providerName);
48 
49  cameraImages = new CByteImage*[2];
50  cameraImages[0] = visionx::tools::createByteImage(imageProviderInfo);
51  cameraImages[1] = visionx::tools::createByteImage(imageProviderInfo);
52 
53  //Only one result image (left camera image)
54  enableResultImages(
55  1, imageProviderPrx->getImageFormat().dimension, imageProviderPrx->getImageFormat().type);
56 }
57 
58 void
60 {
61  std::unique_lock lock(imageMutex);
62  while (oldImages.size() != 0)
63  {
64  oldImages.front().release();
65  oldImages.pop();
66  }
67  deltaTransformations.clear();
68  prevFeatures.clear();
69  prev.release();
70  prevGrey.release();
71  prevTransform.release();
72 }
73 
74 void
76 {
77  std::unique_lock lock(imageMutex);
78 
79  if (!waitForImages(getProperty<std::string>("providerName").getValue(), 1000))
80  {
81  ARMARX_WARNING << "Timeout while waiting for camera images (>1000ms)";
82  return;
83  }
84 
85  armarx::MetaInfoSizeBasePtr info;
86  int numImages =
87  getImages(getProperty<std::string>("providerName").getValue(), cameraImages, info);
88 
89  if (numImages == 0)
90  {
91  ARMARX_WARNING << "Didn't receive one image! Aborting!";
92  return;
93  }
94  //Adapt only left camera image
95  IplImage* ppIplImages[1] = {IplImageAdaptor::Adapt(cameraImages[0])};
96 
97  //convert to grey image
98  cv::Mat currGrey;
99  cv::Mat curr = cv::cvarrToMat(ppIplImages[0]);
100  cv::cvtColor(curr, currGrey, cv::COLOR_BGR2GRAY);
101 
102  cv::Mat tmpImg(curr.size(), curr.type());
103  curr.copyTo(tmpImg);
104 
105  //Parameters for goodFeaturesToTrack function
106  int maxCorners = 100;
107  double qualityLevel = 0.01;
108  double minDistance = 5.0;
109  cv::Mat mask;
110  int blockSize = 3;
111  bool useHarrisDetector = true;
112  double k = 0.04;
113  std::vector<cv::Point2f> currFeatures;
114 
115  cv::goodFeaturesToTrack(currGrey,
116  currFeatures,
117  maxCorners,
118  qualityLevel,
119  minDistance,
120  mask,
121  blockSize,
122  useHarrisDetector,
123  k);
124 
125  //Check if previos image is existing. If not, this is the first image, store it and return
126  if (prev.data == NULL || prevGrey.data == NULL)
127  {
128  ARMARX_LOG << "First image";
129  prev = curr;
130  prevGrey = currGrey;
131  prevFeatures = currFeatures;
132  return;
133  }
134 
135  std::vector<uchar> status;
136  std::vector<float> error;
137 
138  cv::calcOpticalFlowPyrLK(prevGrey,
139  currGrey,
140  prevFeatures,
141  currFeatures,
142  status,
143  error,
144  cv::Size(21, 21),
145  cv::OPTFLOW_USE_INITIAL_FLOW);
146 
147  //Filter only overlapping features for transformation estimation
148  std::vector<cv::Point2f> prevOverlapFeatures, currOverlapFeatures;
149  for (unsigned int i = 0; i < status.size(); i++)
150  {
151  if (status[i] && error[i] < 25.f)
152  {
153  prevOverlapFeatures.push_back(prevFeatures[i]);
154  currOverlapFeatures.push_back(currFeatures[i]);
155  //ARMARX_LOG << deactivateSpam(1) << "error: " << error[i] << " distance: " << std::sqrt(std::pow(prevFeatures[i].x - currFeatures[i].x, 2) + std::pow(prevFeatures[i].y - currFeatures[i].y, 2));
156  }
157  }
158 
159  cv::Mat currTransform =
160  cv::estimateRigidTransform(prevOverlapFeatures, currOverlapFeatures, false);
161 
162  //If no transformation is found, use last valid transformation
163  if (currTransform.data == NULL)
164  {
165  return;
166  //prevTransform.copyTo(currTransform);
167  }
168 
169  oldImages.push(tmpImg);
170 
171  currTransform.copyTo(prevTransform);
172 
173  //Get delta x, y and angle from transformation matrix
174  double dx = currTransform.at<double>(0, 2);
175  double dy = currTransform.at<double>(1, 2);
176  double da = atan2(currTransform.at<double>(1, 0), currTransform.at<double>(0, 0));
177 
178  deltaTransformations.push_back(cv::Vec3d(dx, dy, da));
179 
180  //Check if there are already enough trajectory points to start smoothing
181  if (deltaTransformations.size() <= 2 * SMOOTHING_RADIUS)
182  {
183  while (oldImages.size() > SMOOTHING_RADIUS + 2)
184  {
185  oldImages.pop();
186  }
187  return;
188  }
189 
190  assert(oldImages.size() == SMOOTHING_RADIUS + 2);
191 
192  //index of currently processed frame
193  int currFrameIndex = deltaTransformations.size() - SMOOTHING_RADIUS - 2;
194 
195  cv::Vec3d newTransformation(0, 0, 0);
196 
197  for (int i = -SMOOTHING_RADIUS; i <= SMOOTHING_RADIUS; i++)
198  {
199  newTransformation += -deltaTransformations[currFrameIndex + i];
200  }
201 
202  newTransformation /= ((2 * SMOOTHING_RADIUS) + 1);
203 
204  //create transformation matrix
205  cv::Mat transform(2, 3, CV_64F);
206  transform.at<double>(0, 0) = cos(newTransformation[2]);
207  transform.at<double>(0, 1) = -sin(newTransformation[2]);
208  transform.at<double>(1, 0) = sin(newTransformation[2]);
209  transform.at<double>(1, 1) = cos(newTransformation[2]);
210 
211  transform.at<double>(0, 2) = newTransformation[0];
212  transform.at<double>(1, 2) = newTransformation[1];
213 
214  cv::Mat currImage = oldImages.front();
215  oldImages.pop();
216  cv::Mat result;
217 
218  //apply transformation to image
219  cv::warpAffine(currImage, result, transform, currImage.size());
220 
221  if (drawFeatures)
222  {
223  for (const cv::Point2f& p : prevOverlapFeatures)
224  {
225  cv::circle(result, p, 2, CV_RGB(0, 255, 0), -1);
226  }
227  }
228 
229 
230  CByteImage* output_cimage = new CByteImage();
231  visionx::copyCvMatToIVT(result, output_cimage);
232 
233  CByteImage* resultImages[1] = {output_cimage};
234  provideResultImages(std::move(resultImages));
235 }
236 
239 {
242 }
243 
244 void
246 {
247  std::unique_lock lock(imageMutex);
248  ARMARX_LOG << "Resetting OpenCV stabilization!";
249  while (oldImages.size() != 0)
250  {
251  oldImages.pop();
252  }
253  deltaTransformations.clear();
254  prevFeatures.clear();
255  prev.data = NULL;
256  prevGrey.data = NULL;
257  prevTransform.data = NULL;
258 }
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:737
OpenCVImageStabilizer.h
armarx::OpenCVImageStabilizer::resetOpenCVStabilization
void resetOpenCVStabilization(const Ice::Current &) override
Definition: OpenCVImageStabilizer.cpp:245
armarx::OpenCVImageStabilizer::onInitImageProcessor
void onInitImageProcessor() override
Setup the vision component.
Definition: OpenCVImageStabilizer.cpp:32
visionx::tools::createByteImage
CByteImage * createByteImage(const ImageFormatInfo &imageFormat, const ImageType imageType)
Creates a ByteImage for the destination type specified in the given imageProviderInfo.
visionx::ImageProviderInfo
Definition: ImageProcessor.h:479
armarx::status
status
Definition: FiniteStateMachine.h:244
armarx::OpenCVImageStabilizer::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: OpenCVImageStabilizer.cpp:238
armarx::OpenCVImageStabilizerPropertyDefinitions
Definition: OpenCVImageStabilizer.h:45
SMOOTHING_RADIUS
#define SMOOTHING_RADIUS
Definition: OpenCVImageStabilizer.cpp:27
ARMARX_LOG
#define ARMARX_LOG
Definition: Logging.h:165
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT >>
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:351
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:79
armarx::OpenCVImageStabilizer::onConnectImageProcessor
void onConnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component is fully initia...
Definition: OpenCVImageStabilizer.cpp:42
armarx::OpenCVImageStabilizer::process
void process() override
Process the vision component.
Definition: OpenCVImageStabilizer.cpp:75
visionx::copyCvMatToIVT
void copyCvMatToIVT(cv::Mat const &input, CByteImage *output)
Copies the contents of an OpenCV matrix to an IVT CByteImage.
Definition: OpenCVUtil.cpp:12
IceUtil::Handle< class PropertyDefinitionContainer >
OpenCVUtil.h
armarx::OpenCVImageStabilizer::onExitImageProcessor
void onExitImageProcessor() override
Exit the ImapeProcessor component.
Definition: OpenCVImageStabilizer.cpp:59
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::PropertyDefinitionsPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
Definition: forward_declarations.h:35
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27