quality_sample_data.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. """
  2. Quality Sample Data Generator
  3. Utility for generating sample data and images for quality tab demonstration.
  4. """
  5. import numpy as np
  6. from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QPen, QBrush
  7. from PyQt5.QtCore import Qt, QPoint, QRect
  8. import random
  9. import math
  10. class QualitySampleDataGenerator:
  11. """Generator for sample quality assessment data."""
  12. def __init__(self):
  13. self.fruit_colors = [
  14. QColor("#8B4513"), # Brown/orange
  15. QColor("#CD853F"), # Peru
  16. QColor("#DEB887"), # Burlywood
  17. QColor("#F4A460"), # Sandy brown
  18. ]
  19. self.defect_colors = [
  20. QColor("#F39C12"), # Orange for mechanical damage
  21. QColor("#E67E22"), # Carrot for surface blemish
  22. QColor("#E74C3C"), # Red for severe defects
  23. ]
  24. def generate_sample_fruit_top_view(self, width=300, height=250):
  25. """Generate a sample top view fruit image with defects."""
  26. image = QImage(width, height, QImage.Format_RGB32)
  27. image.fill(QColor("#2C3E50"))
  28. painter = QPainter(image)
  29. painter.setRenderHint(QPainter.Antialiasing)
  30. # Draw main fruit
  31. fruit_radius = min(width, height) // 4
  32. center_x = width // 2
  33. center_y = height // 2
  34. # Fruit body with gradient
  35. fruit_color = random.choice(self.fruit_colors)
  36. painter.setBrush(QBrush(fruit_color))
  37. painter.setPen(QPen(QColor("#654321"), 2))
  38. painter.drawEllipse(center_x - fruit_radius, center_y - fruit_radius,
  39. fruit_radius * 2, fruit_radius * 2)
  40. # Add some texture/pattern to fruit
  41. self._add_fruit_texture(painter, center_x, center_y, fruit_radius)
  42. # Add defect markers
  43. defects = self._generate_sample_defects()
  44. for defect in defects:
  45. self._draw_defect_on_fruit(painter, defect, center_x, center_y, fruit_radius)
  46. painter.end()
  47. return image, defects
  48. def generate_sample_fruit_side_view(self, width=300, height=200):
  49. """Generate a sample side view fruit image with shape outline."""
  50. image = QImage(width, height, QImage.Format_RGB32)
  51. image.fill(QColor("#2C3E50"))
  52. painter = QPainter(image)
  53. painter.setRenderHint(QPainter.Antialiasing)
  54. # Draw main fruit (elliptical for side view)
  55. fruit_width = width * 3 // 5
  56. fruit_height = fruit_width * 3 // 4
  57. center_x = width // 2
  58. center_y = height // 2
  59. # Fruit body
  60. fruit_color = random.choice(self.fruit_colors)
  61. painter.setBrush(QBrush(fruit_color))
  62. painter.setPen(QPen(QColor("#654321"), 2))
  63. painter.drawEllipse(center_x - fruit_width // 2, center_y - fruit_height // 2,
  64. fruit_width, fruit_height)
  65. # Draw shape outline (dashed)
  66. painter.setBrush(Qt.NoBrush)
  67. painter.setPen(QPen(QColor("#27AE60"), 2, Qt.DashLine))
  68. painter.drawEllipse(center_x - fruit_width // 2, center_y - fruit_height // 2,
  69. fruit_width, fruit_height)
  70. # Draw symmetry indicators
  71. self._draw_symmetry_indicators(painter, center_x, center_y, fruit_width)
  72. painter.end()
  73. # Generate shape analysis data
  74. symmetry = random.uniform(85, 95)
  75. aspect_ratio = fruit_width / fruit_height
  76. return image, symmetry, aspect_ratio
  77. def generate_thermal_gradient(self, width=300, height=200, base_temp=28.5):
  78. """Generate thermal gradient visualization."""
  79. image = QImage(width, height, QImage.Format_RGB32)
  80. image.fill(QColor("#000000"))
  81. painter = QPainter(image)
  82. painter.setRenderHint(QPainter.Antialiasing)
  83. # Create thermal gradient background
  84. center_x = width // 2
  85. center_y = height // 2
  86. # Main thermal area (elliptical)
  87. thermal_width = width * 3 // 4
  88. thermal_height = height * 2 // 3
  89. # Create radial gradient for thermal effect
  90. radial_gradient = QRadialGradient(center_x, center_y, max(thermal_width, thermal_height) // 2)
  91. # Center is hottest (red), edges cooler
  92. radial_gradient.setColorAt(0.0, QColor("#E74C3C")) # Hot center
  93. radial_gradient.setColorAt(0.7, QColor("#F39C12")) # Medium
  94. radial_gradient.setColorAt(1.0, QColor("#F1C40F")) # Cool edges
  95. painter.setBrush(QBrush(radial_gradient))
  96. painter.setPen(Qt.NoPen)
  97. painter.drawEllipse(center_x - thermal_width // 2, center_y - thermal_height // 2,
  98. thermal_width, thermal_height)
  99. # Add temperature scale overlay
  100. self._add_temperature_scale(painter, width, height, base_temp)
  101. painter.end()
  102. return image, base_temp
  103. def generate_quality_grading_data(self):
  104. """Generate sample quality grading data."""
  105. # Simulate quality metrics
  106. metrics = {
  107. "size_weight": random.uniform(85, 95),
  108. "shape_regularity": random.uniform(87, 94),
  109. "surface_quality": random.uniform(65, 80),
  110. "color_uniformity": random.uniform(78, 88),
  111. "firmness_thermal": 0 # Not available (thermal offline)
  112. }
  113. # Calculate overall score
  114. available_metrics = [v for k, v in metrics.items() if v > 0]
  115. overall_score = sum(available_metrics) / len(available_metrics)
  116. # Determine grade
  117. if overall_score >= 90:
  118. grade = "A"
  119. elif overall_score >= 75:
  120. grade = "B"
  121. else:
  122. grade = "C"
  123. # Generate additional metrics
  124. additional_metrics = {
  125. "estimated_weight": round(random.uniform(150, 220), 1),
  126. "diameter": random.randint(65, 80),
  127. "aspect_ratio": round(random.uniform(0.8, 0.9), 2),
  128. "surface_defect_coverage": round(random.uniform(2, 8), 1),
  129. "processing_time": round(random.uniform(2.0, 3.5), 1)
  130. }
  131. return {
  132. "grade": grade,
  133. "overall_score": round(overall_score, 1),
  134. "metrics": metrics,
  135. "additional_metrics": additional_metrics,
  136. "model_version": "QualityNet v2.8"
  137. }
  138. def _generate_sample_defects(self):
  139. """Generate sample defect data."""
  140. defects = []
  141. # Mechanical damage
  142. defects.append({
  143. 'type': 'Mechanical Damage',
  144. 'x': random.randint(-20, 20),
  145. 'y': random.randint(-20, 20),
  146. 'size': random.randint(6, 12),
  147. 'confidence': round(random.uniform(80, 95), 1),
  148. 'color': '#F39C12',
  149. 'location': 'Top-Left',
  150. 'size_mm2': round(random.uniform(6, 12), 1)
  151. })
  152. # Surface blemish (sometimes)
  153. if random.random() > 0.5:
  154. defects.append({
  155. 'type': 'Surface Blemish',
  156. 'x': random.randint(-15, 25),
  157. 'y': random.randint(-10, 25),
  158. 'size': random.randint(4, 8),
  159. 'confidence': round(random.uniform(70, 85), 1),
  160. 'color': '#E67E22',
  161. 'location': 'Side',
  162. 'size_mm2': round(random.uniform(3, 7), 1)
  163. })
  164. return defects
  165. def _add_fruit_texture(self, painter, center_x, center_y, radius):
  166. """Add texture pattern to fruit."""
  167. # Add some darker spots for texture
  168. for _ in range(5):
  169. spot_x = center_x + random.randint(-radius // 2, radius // 2)
  170. spot_y = center_y + random.randint(-radius // 2, radius // 2)
  171. spot_radius = random.randint(3, 8)
  172. # Only draw if within fruit bounds
  173. distance = math.sqrt((spot_x - center_x) ** 2 + (spot_y - center_y) ** 2)
  174. if distance + spot_radius <= radius:
  175. painter.setBrush(QBrush(QColor("#654321")))
  176. painter.setPen(Qt.NoPen)
  177. painter.drawEllipse(spot_x - spot_radius, spot_y - spot_radius,
  178. spot_radius * 2, spot_radius * 2)
  179. def _draw_defect_on_fruit(self, painter, defect, center_x, center_y, fruit_radius):
  180. """Draw a defect marker on the fruit."""
  181. # Calculate position relative to fruit center
  182. marker_x = center_x + (defect['x'] * fruit_radius // 50)
  183. marker_y = center_y + (defect['y'] * fruit_radius // 50)
  184. # Draw marker circle
  185. marker_radius = max(6, defect['size'])
  186. painter.setBrush(QBrush(QColor(defect['color'])))
  187. painter.setPen(QPen(QColor('#D35400'), 2))
  188. painter.drawEllipse(marker_x - marker_radius, marker_y - marker_radius,
  189. marker_radius * 2, marker_radius * 2)
  190. # Draw confidence text
  191. painter.setPen(QPen(QColor('white'), 1))
  192. painter.drawText(marker_x - 15, marker_y - marker_radius - 3,
  193. f"{defect['confidence']}%")
  194. def _draw_symmetry_indicators(self, painter, center_x, center_y, fruit_width):
  195. """Draw symmetry indicator lines."""
  196. # Draw symmetry axis lines
  197. symmetry_y = center_y
  198. line_length = fruit_width // 6
  199. painter.setPen(QPen(QColor("#3498DB"), 2, Qt.SolidLine))
  200. # Left indicator
  201. left_x = center_x - fruit_width // 4
  202. painter.drawLine(left_x, symmetry_y - line_length, left_x, symmetry_y + line_length)
  203. # Right indicator
  204. right_x = center_x + fruit_width // 4
  205. painter.drawLine(right_x, symmetry_y - line_length, right_x, symmetry_y + line_length)
  206. def _add_temperature_scale(self, painter, width, height, base_temp):
  207. """Add temperature scale overlay."""
  208. # Temperature scale at bottom
  209. scale_width = width * 3 // 4
  210. scale_height = 15
  211. scale_x = (width - scale_width) // 2
  212. scale_y = height - 30
  213. # Draw scale background
  214. painter.setBrush(QBrush(QColor("#34495E")))
  215. painter.setPen(Qt.NoPen)
  216. painter.drawRect(scale_x, scale_y, scale_width, scale_height)
  217. # Draw temperature gradient in scale
  218. temp_gradient = QBrush()
  219. temp_gradient = QLinearGradient(scale_x, 0, scale_x + scale_width, 0)
  220. temp_gradient.setColorAt(0.0, QColor("#3498DB")) # Cool (22°C)
  221. temp_gradient.setColorAt(0.25, QColor("#27AE60"))
  222. temp_gradient.setColorAt(0.5, QColor("#F1C40F"))
  223. temp_gradient.setColorAt(0.75, QColor("#E67E22"))
  224. temp_gradient.setColorAt(1.0, QColor("#E74C3C")) # Hot (32°C)
  225. painter.setBrush(temp_gradient)
  226. painter.drawRect(scale_x, scale_y, scale_width, scale_height)
  227. # Draw scale border
  228. painter.setPen(QPen(QColor("#ECF0F1"), 1))
  229. painter.drawRect(scale_x, scale_y, scale_width, scale_height)
  230. # Draw temperature labels
  231. painter.setPen(QPen(QColor("white"), 1))
  232. min_temp = "22°C"
  233. max_temp = "32°C"
  234. current_temp = f"{base_temp}°C"
  235. # Min temperature
  236. painter.drawText(scale_x + 5, scale_y - 2, min_temp)
  237. # Current temperature (centered)
  238. current_rect = QRect(scale_x + scale_width // 2 - 20, scale_y - 18, 40, 15)
  239. painter.drawText(current_rect, Qt.AlignCenter, current_temp)
  240. # Max temperature
  241. max_temp_rect = QRect(scale_x + scale_width - 35, scale_y - 2, 30, 12)
  242. painter.drawText(max_temp_rect, Qt.AlignRight, max_temp)
  243. # Global instance for easy access
  244. sample_data_generator = QualitySampleDataGenerator()
  245. def get_sample_fruit_top_view():
  246. """Get a sample top view image and defects data."""
  247. return sample_data_generator.generate_sample_fruit_top_view()
  248. def get_sample_fruit_side_view():
  249. """Get a sample side view image and analysis data."""
  250. return sample_data_generator.generate_sample_fruit_side_view()
  251. def get_thermal_gradient():
  252. """Get a thermal gradient visualization."""
  253. return sample_data_generator.generate_thermal_gradient()
  254. def get_quality_grading_data():
  255. """Get sample quality grading data."""
  256. return sample_data_generator.generate_quality_grading_data()
  257. def generate_defect_detection_results():
  258. """Generate sample defect detection results."""
  259. defects = sample_data_generator._generate_sample_defects()
  260. # Convert to format expected by panels
  261. formatted_defects = []
  262. for defect in defects:
  263. formatted_defects.append({
  264. 'type': defect['type'],
  265. 'location': defect['location'],
  266. 'size': f"{defect['size_mm2']}mm²",
  267. 'confidence': defect['confidence'],
  268. 'color': defect['color'],
  269. 'category': 'warning'
  270. })
  271. # Add shape analysis
  272. formatted_defects.append({
  273. 'type': 'Shape Analysis',
  274. 'result': 'Regular',
  275. 'symmetry': '91.2%',
  276. 'confidence': 94.1,
  277. 'color': '#27ae60',
  278. 'category': 'success'
  279. })
  280. # Add locule count
  281. formatted_defects.append({
  282. 'type': 'Locule Count',
  283. 'count': '4',
  284. 'confidence': 94.5,
  285. 'color': '#3498db',
  286. 'category': 'info'
  287. })
  288. return formatted_defects