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"
25 
26 #define SMOOTHING_RADIUS 3
27 
28 using namespace armarx;
29 
31 {
32  providerName = getProperty<std::string>("providerName").getValue();
33  usingImageProvider(providerName);
34 
35  frameRate = getProperty<float>("Framerate").getValue();
36  drawFeatures = getProperty<bool>("drawFeatures").getValue();
37 }
38 
40 {
41  std::unique_lock lock(imageMutex);
42 
43  visionx::ImageProviderInfo imageProviderInfo = getImageProvider(providerName);
44  imageProviderPrx = getProxy<visionx::ImageProviderInterfacePrx>(providerName);
45 
46  cameraImages = new CByteImage*[2];
47  cameraImages[0] = visionx::tools::createByteImage(imageProviderInfo);
48  cameraImages[1] = visionx::tools::createByteImage(imageProviderInfo);
49 
50  //Only one result image (left camera image)
51  enableResultImages(1, imageProviderPrx->getImageFormat().dimension, imageProviderPrx->getImageFormat().type);
52 
53 }
54 
56 {
57  std::unique_lock lock(imageMutex);
58  while (oldImages.size() != 0)
59  {
60  oldImages.front().release();
61  oldImages.pop();
62  }
63  deltaTransformations.clear();
64  prevFeatures.clear();
65  prev.release();
66  prevGrey.release();
67  prevTransform.release();
68 }
69 
71 {
72  std::unique_lock lock(imageMutex);
73 
74  if (!waitForImages(getProperty<std::string>("providerName").getValue(), 1000))
75  {
76  ARMARX_WARNING << "Timeout while waiting for camera images (>1000ms)";
77  return;
78  }
79 
80  armarx::MetaInfoSizeBasePtr info;
81  int numImages = getImages(getProperty<std::string>("providerName").getValue(), cameraImages, info);
82 
83  if (numImages == 0)
84  {
85  ARMARX_WARNING << "Didn't receive one image! Aborting!";
86  return;
87  }
88  //Adapt only left camera image
89  IplImage* ppIplImages[1] = { IplImageAdaptor::Adapt(cameraImages[0]) };
90 
91  //convert to grey image
92  cv::Mat currGrey;
93  cv::Mat curr = cv::cvarrToMat(ppIplImages[0]);
94  cv::cvtColor(curr, currGrey, cv::COLOR_BGR2GRAY);
95 
96  cv::Mat tmpImg(curr.size(), curr.type());
97  curr.copyTo(tmpImg);
98 
99  //Parameters for goodFeaturesToTrack function
100  int maxCorners = 100;
101  double qualityLevel = 0.01;
102  double minDistance = 5.0;
103  cv::Mat mask;
104  int blockSize = 3;
105  bool useHarrisDetector = true;
106  double k = 0.04;
107  std::vector< cv::Point2f > currFeatures;
108 
109  cv::goodFeaturesToTrack(currGrey, currFeatures, maxCorners, qualityLevel, minDistance, mask, blockSize, useHarrisDetector, k);
110 
111  //Check if previos image is existing. If not, this is the first image, store it and return
112  if (prev.data == NULL || prevGrey.data == NULL)
113  {
114  ARMARX_LOG << "First image";
115  prev = curr;
116  prevGrey = currGrey;
117  prevFeatures = currFeatures;
118  return;
119  }
120 
121  std::vector<uchar> status;
122  std::vector<float> error;
123 
124  cv::calcOpticalFlowPyrLK(prevGrey, currGrey, prevFeatures, currFeatures, status, error, cv::Size(21, 21), cv::OPTFLOW_USE_INITIAL_FLOW);
125 
126  //Filter only overlapping features for transformation estimation
127  std::vector< cv::Point2f > prevOverlapFeatures, currOverlapFeatures;
128  for (unsigned int i = 0; i < status.size(); i++)
129  {
130  if (status[i] && error[i] < 25.f)
131  {
132  prevOverlapFeatures.push_back(prevFeatures[i]);
133  currOverlapFeatures.push_back(currFeatures[i]);
134  //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));
135  }
136  }
137 
138  cv::Mat currTransform = cv::estimateRigidTransform(prevOverlapFeatures, currOverlapFeatures, false);
139 
140  //If no transformation is found, use last valid transformation
141  if (currTransform.data == NULL)
142  {
143  return;
144  //prevTransform.copyTo(currTransform);
145  }
146 
147  oldImages.push(tmpImg);
148 
149  currTransform.copyTo(prevTransform);
150 
151  //Get delta x, y and angle from transformation matrix
152  double dx = currTransform.at<double>(0, 2);
153  double dy = currTransform.at<double>(1, 2);
154  double da = atan2(currTransform.at<double>(1, 0), currTransform.at<double>(0, 0));
155 
156  deltaTransformations.push_back(cv::Vec3d(dx, dy, da));
157 
158  //Check if there are already enough trajectory points to start smoothing
159  if (deltaTransformations.size() <= 2 * SMOOTHING_RADIUS)
160  {
161  while (oldImages.size() > SMOOTHING_RADIUS + 2)
162  {
163  oldImages.pop();
164  }
165  return;
166  }
167 
168  assert(oldImages.size() == SMOOTHING_RADIUS + 2);
169 
170  //index of currently processed frame
171  int currFrameIndex = deltaTransformations.size() - SMOOTHING_RADIUS - 2;
172 
173  cv::Vec3d newTransformation(0, 0, 0);
174 
175  for (int i = -SMOOTHING_RADIUS; i <= SMOOTHING_RADIUS; i++)
176  {
177  newTransformation += -deltaTransformations[currFrameIndex + i];
178  }
179 
180  newTransformation /= ((2 * SMOOTHING_RADIUS) + 1);
181 
182  //create transformation matrix
183  cv::Mat transform(2, 3, CV_64F);
184  transform.at<double>(0, 0) = cos(newTransformation[2]);
185  transform.at<double>(0, 1) = -sin(newTransformation[2]);
186  transform.at<double>(1, 0) = sin(newTransformation[2]);
187  transform.at<double>(1, 1) = cos(newTransformation[2]);
188 
189  transform.at<double>(0, 2) = newTransformation[0];
190  transform.at<double>(1, 2) = newTransformation[1];
191 
192  cv::Mat currImage = oldImages.front();
193  oldImages.pop();
194  cv::Mat result;
195 
196  //apply transformation to image
197  cv::warpAffine(currImage, result, transform, currImage.size());
198 
199  if (drawFeatures)
200  {
201  for (const cv::Point2f& p : prevOverlapFeatures)
202  {
203  cv::circle(result, p, 2, CV_RGB(0, 255, 0), -1);
204  }
205  }
206 
207 
208  CByteImage* output_cimage = new CByteImage();
209  visionx::copyCvMatToIVT(result, output_cimage);
210 
211  CByteImage* resultImages[1] = {output_cimage};
212  provideResultImages(std::move(resultImages));
213 }
214 
216 {
219 }
220 
221 
223 {
224  std::unique_lock lock(imageMutex);
225  ARMARX_LOG << "Resetting OpenCV stabilization!";
226  while (oldImages.size() != 0)
227  {
228  oldImages.pop();
229  }
230  deltaTransformations.clear();
231  prevFeatures.clear();
232  prev.data = NULL;
233  prevGrey.data = NULL;
234  prevTransform.data = NULL;
235 }
OpenCVImageStabilizer.h
armarx::OpenCVImageStabilizer::resetOpenCVStabilization
void resetOpenCVStabilization(const Ice::Current &) override
Definition: OpenCVImageStabilizer.cpp:222
armarx::OpenCVImageStabilizer::onInitImageProcessor
void onInitImageProcessor() override
Setup the vision component.
Definition: OpenCVImageStabilizer.cpp:30
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:466
armarx::status
status
Definition: FiniteStateMachine.h:259
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:695
armarx::OpenCVImageStabilizer::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: OpenCVImageStabilizer.cpp:215
armarx::OpenCVImageStabilizerPropertyDefinitions
Definition: OpenCVImageStabilizer.h:46
SMOOTHING_RADIUS
#define SMOOTHING_RADIUS
Definition: OpenCVImageStabilizer.cpp:26
ARMARX_LOG
#define ARMARX_LOG
Definition: Logging.h:163
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:74
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:39
armarx::OpenCVImageStabilizer::process
void process() override
Process the vision component.
Definition: OpenCVImageStabilizer.cpp:70
visionx::copyCvMatToIVT
void copyCvMatToIVT(cv::Mat const &input, CByteImage *output)
Copies the contents of an OpenCV matrix to an IVT CByteImage.
Definition: OpenCVUtil.cpp:13
IceUtil::Handle< class PropertyDefinitionContainer >
OpenCVUtil.h
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:315
armarx::OpenCVImageStabilizer::onExitImageProcessor
void onExitImageProcessor() override
Exit the ImapeProcessor component.
Definition: OpenCVImageStabilizer.cpp:55
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::PropertyDefinitionsPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
Definition: forward_declarations.h:34
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28