GPUImageLuminosity.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #import "GPUImageLuminosity.h"
  2. #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  3. NSString *const kGPUImageInitialLuminosityFragmentShaderString = SHADER_STRING
  4. (
  5. precision highp float;
  6. uniform sampler2D inputImageTexture;
  7. varying highp vec2 outputTextureCoordinate;
  8. varying highp vec2 upperLeftInputTextureCoordinate;
  9. varying highp vec2 upperRightInputTextureCoordinate;
  10. varying highp vec2 lowerLeftInputTextureCoordinate;
  11. varying highp vec2 lowerRightInputTextureCoordinate;
  12. const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
  13. void main()
  14. {
  15. highp float upperLeftLuminance = dot(texture2D(inputImageTexture, upperLeftInputTextureCoordinate).rgb, W);
  16. highp float upperRightLuminance = dot(texture2D(inputImageTexture, upperRightInputTextureCoordinate).rgb, W);
  17. highp float lowerLeftLuminance = dot(texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).rgb, W);
  18. highp float lowerRightLuminance = dot(texture2D(inputImageTexture, lowerRightInputTextureCoordinate).rgb, W);
  19. highp float luminosity = 0.25 * (upperLeftLuminance + upperRightLuminance + lowerLeftLuminance + lowerRightLuminance);
  20. gl_FragColor = vec4(luminosity, luminosity, luminosity, 1.0);
  21. }
  22. );
  23. NSString *const kGPUImageLuminosityFragmentShaderString = SHADER_STRING
  24. (
  25. precision highp float;
  26. uniform sampler2D inputImageTexture;
  27. varying highp vec2 outputTextureCoordinate;
  28. varying highp vec2 upperLeftInputTextureCoordinate;
  29. varying highp vec2 upperRightInputTextureCoordinate;
  30. varying highp vec2 lowerLeftInputTextureCoordinate;
  31. varying highp vec2 lowerRightInputTextureCoordinate;
  32. void main()
  33. {
  34. highp float upperLeftLuminance = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
  35. highp float upperRightLuminance = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
  36. highp float lowerLeftLuminance = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
  37. highp float lowerRightLuminance = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
  38. highp float luminosity = 0.25 * (upperLeftLuminance + upperRightLuminance + lowerLeftLuminance + lowerRightLuminance);
  39. gl_FragColor = vec4(luminosity, luminosity, luminosity, 1.0);
  40. }
  41. );
  42. #else
  43. NSString *const kGPUImageInitialLuminosityFragmentShaderString = SHADER_STRING
  44. (
  45. uniform sampler2D inputImageTexture;
  46. varying vec2 outputTextureCoordinate;
  47. varying vec2 upperLeftInputTextureCoordinate;
  48. varying vec2 upperRightInputTextureCoordinate;
  49. varying vec2 lowerLeftInputTextureCoordinate;
  50. varying vec2 lowerRightInputTextureCoordinate;
  51. const vec3 W = vec3(0.2125, 0.7154, 0.0721);
  52. void main()
  53. {
  54. float upperLeftLuminance = dot(texture2D(inputImageTexture, upperLeftInputTextureCoordinate).rgb, W);
  55. float upperRightLuminance = dot(texture2D(inputImageTexture, upperRightInputTextureCoordinate).rgb, W);
  56. float lowerLeftLuminance = dot(texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).rgb, W);
  57. float lowerRightLuminance = dot(texture2D(inputImageTexture, lowerRightInputTextureCoordinate).rgb, W);
  58. float luminosity = 0.25 * (upperLeftLuminance + upperRightLuminance + lowerLeftLuminance + lowerRightLuminance);
  59. gl_FragColor = vec4(luminosity, luminosity, luminosity, 1.0);
  60. }
  61. );
  62. NSString *const kGPUImageLuminosityFragmentShaderString = SHADER_STRING
  63. (
  64. uniform sampler2D inputImageTexture;
  65. varying vec2 outputTextureCoordinate;
  66. varying vec2 upperLeftInputTextureCoordinate;
  67. varying vec2 upperRightInputTextureCoordinate;
  68. varying vec2 lowerLeftInputTextureCoordinate;
  69. varying vec2 lowerRightInputTextureCoordinate;
  70. void main()
  71. {
  72. float upperLeftLuminance = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
  73. float upperRightLuminance = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
  74. float lowerLeftLuminance = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
  75. float lowerRightLuminance = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
  76. float luminosity = 0.25 * (upperLeftLuminance + upperRightLuminance + lowerLeftLuminance + lowerRightLuminance);
  77. gl_FragColor = vec4(luminosity, luminosity, luminosity, 1.0);
  78. }
  79. );
  80. #endif
  81. @implementation GPUImageLuminosity
  82. @synthesize luminosityProcessingFinishedBlock = _luminosityProcessingFinishedBlock;
  83. #pragma mark -
  84. #pragma mark Initialization and teardown
  85. - (id)init;
  86. {
  87. if (!(self = [super initWithVertexShaderFromString:kGPUImageColorAveragingVertexShaderString fragmentShaderFromString:kGPUImageInitialLuminosityFragmentShaderString]))
  88. {
  89. return nil;
  90. }
  91. texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
  92. texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
  93. __unsafe_unretained GPUImageLuminosity *weakSelf = self;
  94. [self setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
  95. [weakSelf extractLuminosityAtFrameTime:frameTime];
  96. }];
  97. runSynchronouslyOnVideoProcessingQueue(^{
  98. [GPUImageContext useImageProcessingContext];
  99. secondFilterProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageColorAveragingVertexShaderString fragmentShaderString:kGPUImageLuminosityFragmentShaderString];
  100. if (!secondFilterProgram.initialized)
  101. {
  102. [self initializeSecondaryAttributes];
  103. if (![secondFilterProgram link])
  104. {
  105. NSString *progLog = [secondFilterProgram programLog];
  106. NSLog(@"Program link log: %@", progLog);
  107. NSString *fragLog = [secondFilterProgram fragmentShaderLog];
  108. NSLog(@"Fragment shader compile log: %@", fragLog);
  109. NSString *vertLog = [secondFilterProgram vertexShaderLog];
  110. NSLog(@"Vertex shader compile log: %@", vertLog);
  111. filterProgram = nil;
  112. NSAssert(NO, @"Filter shader link failed");
  113. }
  114. }
  115. secondFilterPositionAttribute = [secondFilterProgram attributeIndex:@"position"];
  116. secondFilterTextureCoordinateAttribute = [secondFilterProgram attributeIndex:@"inputTextureCoordinate"];
  117. secondFilterInputTextureUniform = [secondFilterProgram uniformIndex:@"inputImageTexture"]; // This does assume a name of "inputImageTexture" for the fragment shader
  118. secondFilterInputTextureUniform2 = [secondFilterProgram uniformIndex:@"inputImageTexture2"]; // This does assume a name of "inputImageTexture2" for second input texture in the fragment shader
  119. secondFilterTexelWidthUniform = [secondFilterProgram uniformIndex:@"texelWidth"];
  120. secondFilterTexelHeightUniform = [secondFilterProgram uniformIndex:@"texelHeight"];
  121. [GPUImageContext setActiveShaderProgram:secondFilterProgram];
  122. glEnableVertexAttribArray(secondFilterPositionAttribute);
  123. glEnableVertexAttribArray(secondFilterTextureCoordinateAttribute);
  124. });
  125. return self;
  126. }
  127. - (void)initializeSecondaryAttributes;
  128. {
  129. [secondFilterProgram addAttribute:@"position"];
  130. [secondFilterProgram addAttribute:@"inputTextureCoordinate"];
  131. }
  132. /*
  133. - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
  134. {
  135. if (self.preventRendering)
  136. {
  137. [firstInputFramebuffer unlock];
  138. return;
  139. }
  140. // Do an initial render pass that both convert to luminance and reduces
  141. [GPUImageContext setActiveShaderProgram:filterProgram];
  142. glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
  143. glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
  144. GLuint currentFramebuffer = [[stageFramebuffers objectAtIndex:0] intValue];
  145. glBindFramebuffer(GL_FRAMEBUFFER, currentFramebuffer);
  146. #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  147. CGSize currentStageSize = [[stageSizes objectAtIndex:0] CGSizeValue];
  148. #else
  149. NSSize currentStageSize = [[stageSizes objectAtIndex:0] sizeValue];
  150. #endif
  151. glViewport(0, 0, (int)currentStageSize.width, (int)currentStageSize.height);
  152. GLuint currentTexture = [firstInputFramebuffer texture];
  153. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  154. glClear(GL_COLOR_BUFFER_BIT);
  155. glActiveTexture(GL_TEXTURE2);
  156. glBindTexture(GL_TEXTURE_2D, currentTexture);
  157. glUniform1i(filterInputTextureUniform, 2);
  158. glUniform1f(texelWidthUniform, 0.5 / currentStageSize.width);
  159. glUniform1f(texelHeightUniform, 0.5 / currentStageSize.height);
  160. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  161. currentTexture = [[stageTextures objectAtIndex:0] intValue];
  162. // Just perform reductions from this point on
  163. [GPUImageContext setActiveShaderProgram:secondFilterProgram];
  164. glVertexAttribPointer(secondFilterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
  165. glVertexAttribPointer(secondFilterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
  166. NSUInteger numberOfStageFramebuffers = [stageFramebuffers count];
  167. for (NSUInteger currentStage = 1; currentStage < numberOfStageFramebuffers; currentStage++)
  168. {
  169. currentFramebuffer = [[stageFramebuffers objectAtIndex:currentStage] intValue];
  170. glBindFramebuffer(GL_FRAMEBUFFER, currentFramebuffer);
  171. #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  172. currentStageSize = [[stageSizes objectAtIndex:currentStage] CGSizeValue];
  173. #else
  174. currentStageSize = [[stageSizes objectAtIndex:currentStage] sizeValue];
  175. #endif
  176. glViewport(0, 0, (int)currentStageSize.width, (int)currentStageSize.height);
  177. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  178. glClear(GL_COLOR_BUFFER_BIT);
  179. glActiveTexture(GL_TEXTURE2);
  180. glBindTexture(GL_TEXTURE_2D, currentTexture);
  181. glUniform1i(secondFilterInputTextureUniform, 2);
  182. glUniform1f(secondFilterTexelWidthUniform, 0.5 / currentStageSize.width);
  183. glUniform1f(secondFilterTexelHeightUniform, 0.5 / currentStageSize.height);
  184. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  185. currentTexture = [[stageTextures objectAtIndex:currentStage] intValue];
  186. // NSUInteger totalBytesForImage = (int)currentStageSize.width * (int)currentStageSize.height * 4;
  187. // GLubyte *rawImagePixels2 = (GLubyte *)malloc(totalBytesForImage);
  188. // glReadPixels(0, 0, (int)currentStageSize.width, (int)currentStageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels2);
  189. // CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rawImagePixels2, totalBytesForImage, NULL);
  190. // CGColorSpaceRef defaultRGBColorSpace = CGColorSpaceCreateDeviceRGB();
  191. //
  192. // CGFloat currentRedTotal = 0.0f, currentGreenTotal = 0.0f, currentBlueTotal = 0.0f, currentAlphaTotal = 0.0f;
  193. // NSUInteger totalNumberOfPixels = totalBytesForImage / 4;
  194. //
  195. // for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
  196. // {
  197. // currentRedTotal += (CGFloat)rawImagePixels2[(currentPixel * 4)] / 255.0f;
  198. // currentGreenTotal += (CGFloat)rawImagePixels2[(currentPixel * 4) + 1] / 255.0f;
  199. // currentBlueTotal += (CGFloat)rawImagePixels2[(currentPixel * 4 + 2)] / 255.0f;
  200. // currentAlphaTotal += (CGFloat)rawImagePixels2[(currentPixel * 4) + 3] / 255.0f;
  201. // }
  202. //
  203. // NSLog(@"Stage %d average image red: %f, green: %f, blue: %f, alpha: %f", currentStage, currentRedTotal / (CGFloat)totalNumberOfPixels, currentGreenTotal / (CGFloat)totalNumberOfPixels, currentBlueTotal / (CGFloat)totalNumberOfPixels, currentAlphaTotal / (CGFloat)totalNumberOfPixels);
  204. //
  205. //
  206. // CGImageRef cgImageFromBytes = CGImageCreate((int)currentStageSize.width, (int)currentStageSize.height, 8, 32, 4 * (int)currentStageSize.width, defaultRGBColorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaLast, dataProvider, NULL, NO, kCGRenderingIntentDefault);
  207. //
  208. // UIImage *imageToSave = [UIImage imageWithCGImage:cgImageFromBytes];
  209. //
  210. // NSData *dataForPNGFile = UIImagePNGRepresentation(imageToSave);
  211. //
  212. // NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  213. // NSString *documentsDirectory = [paths objectAtIndex:0];
  214. //
  215. // NSString *imageName = [NSString stringWithFormat:@"AverageLevel%d.png", currentStage];
  216. // NSError *error = nil;
  217. // if (![dataForPNGFile writeToFile:[documentsDirectory stringByAppendingPathComponent:imageName] options:NSAtomicWrite error:&error])
  218. // {
  219. // return;
  220. // }
  221. }
  222. [firstInputFramebuffer unlock];
  223. }
  224. */
  225. #pragma mark -
  226. #pragma mark Callbacks
  227. - (void)extractLuminosityAtFrameTime:(CMTime)frameTime;
  228. {
  229. runSynchronouslyOnVideoProcessingQueue(^{
  230. // we need a normal color texture for this filter
  231. NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture format for this filter must be GL_RGBA.");
  232. NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
  233. NSUInteger totalNumberOfPixels = round(finalStageSize.width * finalStageSize.height);
  234. if (rawImagePixels == NULL)
  235. {
  236. rawImagePixels = (GLubyte *)malloc(totalNumberOfPixels * 4);
  237. }
  238. [GPUImageContext useImageProcessingContext];
  239. [outputFramebuffer activateFramebuffer];
  240. glReadPixels(0, 0, (int)finalStageSize.width, (int)finalStageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
  241. NSUInteger luminanceTotal = 0;
  242. NSUInteger byteIndex = 0;
  243. for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
  244. {
  245. luminanceTotal += rawImagePixels[byteIndex];
  246. byteIndex += 4;
  247. }
  248. CGFloat normalizedLuminosityTotal = (CGFloat)luminanceTotal / (CGFloat)totalNumberOfPixels / 255.0;
  249. if (_luminosityProcessingFinishedBlock != NULL)
  250. {
  251. _luminosityProcessingFinishedBlock(normalizedLuminosityTotal, frameTime);
  252. }
  253. });
  254. }
  255. @end